美文网首页
基于Sonarqube Api---Jenkins集成发布质量控

基于Sonarqube Api---Jenkins集成发布质量控

作者: 上岸的魚 | 来源:发表于2020-04-25 12:58 被阅读0次

    接上文https://www.jianshu.com/p/5e1ccf999c24,我们安装了sonarqube,并完成了对java/.net代码扫描,之后通过jenkins集成实现了自动化扫描。
    本文我们将研究如何通过sonarqubeApi实现发布流程质量控制

    1.Sonarqube Api介绍

    sonarqube开放了很多api接口,用户及权限,项目及扫描结果,还有相关系统及配置等api。
    api清单可以在sonar平台直接查看,默认地址为:http://localhost:9000/web_api/,localhost:9000是sonar的访问路径需要替换成你的地址,笔者的是绑定了域名的sonar.xxx.cn

    SonarqubeApi页面

    2.获取扫描结果并与Jenkins集成控制发布质量

    上文我们通过Jenkins实现了自动化扫描,但我们并没有对扫描结果进行获取或处理,这样导致整个扫描在整体发布流程中变成一个可有可无的弱势环节。
    接下来我们将提升扫描环节的重要度,通过对其结果获取控制发布流程,思路发下:
    1.通过api获取我们所需要采集的扫描指标值(每公司或团队关注的指标会有略有差异)
    2.对指标质量规则建模,实现控制方案的数据模型
    3.包装成一个可直接使用的接口供jenkins集成
    4.jenkins通过调用我们包装好的接口获取扫描结果,并根据结果控制流程
    步骤1:API获取指标
    通过对API分析,我们找到如下获取指标的API
    http://sonar.xxxx.cn/api/measures/search?projectKeys=项目 key&metricKeys=code_smells,bugs,coverage,duplicated_lines_density,ncloc,security_rating,vulnerabilities,comment_lines_density
    调用后返回结果如下:

    {
        "measures": [{
            "metric": "bugs",
            "value": "0",
            "component": "netCore01",
            "bestValue": true
        }, {
            "metric": "code_smells",
            "value": "3",
            "component": "netCore01",
            "bestValue": false
        }, {
            "metric": "comment_lines_density",
            "value": "2.8",
            "component": "netCore01",
            "bestValue": false
        }, {
            "metric": "coverage",
            "value": "0.0",
            "component": "netCore01",
            "bestValue": false
        }, {
            "metric": "duplicated_lines_density",
            "value": "0.0",
            "component": "netCore01",
            "bestValue": true
        }, {
            "metric": "ncloc",
            "value": "138",
            "component": "netCore01"
        }, {
            "metric": "security_rating",
            "value": "1.0",
            "component": "netCore01",
            "bestValue": true
        }, {
            "metric": "vulnerabilities",
            "value": "0",
            "component": "netCore01",
            "bestValue": true
        }]
    }
    说明:具体指标获取可以在项目--指标然后左侧各种指标点击查看,URL中会有该指标的字段名
    

    步骤2:质量规则建模
    以上接口我们获取了一些主要的扫描结果,但这些指标数据如何控制更合理,是每个团队需要考虑的事情,本例为演示技术实现为主,建模不具实际业务参考。
    强制规则:首先我们认为BUG及安全漏洞是系统不能允许出现的,一旦有这两项,则需要先修复才能部署
    扣分规则:不同的问题类型我们扣分权重不同,通过加权*数量进行扣分计算
    通过阈值:如果强制规则通过,则验证最低通过分阈值判断是否通过

    策略 是否通过 扣分 对应字段
    漏洞大于0 漏洞数*5 vulnerabilities 安全漏洞数量
    BUG大于0 BUG数*5 bugs BUG数量
    code_smells数量 扣分 code_smells*2 code_smells 代码坏味道数量
    duplicated_lines_density 扣分 duplicated_lines_density>5%则单独扣分规则 duplicated_lines_density重复行比例
    comment_lines_density 扣分 comment_lines_density<7%则单独扣分规则 comment_lines_density代码注释比例

    还有其他几个指标,不再逐一讲解。
    步骤3:对数据模型及sonar Api包装成专用api
    服务接口采用.netCore开发,主要是vs开发工具的确太好用,然后用k8s部署即可
    主要代码逻辑如下:
    API层:

        [Route("[controller]/[action]")]
        [ApiController]
        public class SonarController : Controller
        {
            static string fields = "code_smells,bugs,coverage,duplicated_lines_density,ncloc,security_rating,vulnerabilities,comment_lines_density";
            static string sonarRootUrl = "http://sonarqube.default:9000";
            static float minScore = 70f;  //分数通过阈值
            public SonarResult Scan(string projectKey)
            {
                string url = string.Format(sonarRootUrl + "/api/measures/search?projectKeys=" + projectKey + "&metricKeys=" + fields);
                string result = HttpHelper.HttpGet(url, "application/json");
    
                SonarScanResult scanResult = result.FromJSON<SonarScanResult>();
                return new SonarResult(scanResult,minScore);
            }
        }
    

    数据处理部分

     public class SonarScanResult
        {
            public List<SonarScanResultItem> measures { get; set; }
        }
        public class SonarScanResultItem
        {
            public string metric { get; set; }
            public string value { get; set; }
            public string component { get; set; }
            public bool bestValue { get; set; }
        }
    
    
        public class SonarResult
        {
            public List<SonarScanResultItem> measures;
    
            /// <summary>
            /// 坏味道
            /// </summary>
            public float code_smells { get; set; }
    
            /// <summary>
            /// BUG
            /// </summary>
            public float bugs { get; set; }
            /// <summary>
            /// 覆盖率
            /// </summary>
            public float coverage { get; set; }
            /// <summary>
            /// 重复率
            /// </summary>
            public float duplicated_lines_density { get; set; }
            /// <summary>
            /// 代码行数
            /// </summary>
            public float ncloc { get; set; }  //代码行数
            /// <summary>
            /// 
            /// </summary>
            public float security_rating { get; set; }
            /// <summary>
            /// 漏洞数量
            /// </summary>
            public float vulnerabilities { get; set; }
    
            /// <summary>
            /// 可靠性
            /// </summary>
            public float reliability_rating { get; set; }
    
            /// <summary>
            /// 注释占比
            /// </summary>
            public float? comment_lines_density { get; set; }
    
            /// <summary>
            /// 总体得分
            /// </summary>
            public float Score { get; set; }
            /// <summary>
            /// 是否通过
            /// </summary>
            public bool Pass { get; set; }
            public string Msg { get; set; }
            private float minScore;
            public SonarResult(SonarScanResult result, float minScore)
            {
                this.minScore = minScore;
                if (result != null && result.measures != null)
                {
                    this.measures = result.measures;
                }
                else
                {
                    this.measures = new List<SonarScanResultItem>();
                }
                this.OutputResult();
            }
            /// <summary>
            /// 计算输出结果
            /// </summary>
            public void OutputResult()
            {
                this.code_smells = GetMetricValue("code_smells");
                this.bugs = GetMetricValue("bugs");
                this.coverage = GetMetricValue("coverage");
                this.duplicated_lines_density = GetMetricValue("duplicated_lines_density");
                this.ncloc = GetMetricValue("ncloc");
                this.security_rating = GetMetricValue("security_rating");
                this.vulnerabilities = GetMetricValue("vulnerabilities");
                this.reliability_rating = GetMetricValue("reliability_rating");
                comment_lines_density = GetMetricValue2("comment_lines_density");
                this.Pass = true;
                if (bugs > 0 || vulnerabilities > 0)
                {
                    //强制不通过,分数0,别怪我为难程序员,我也是被刁难了多年
                    this.Pass = false;
                    this.Score = 0;
                    this.Msg = string.Format("漏洞:{0},BUG:{1},不满足发布策略", vulnerabilities, bugs);
                }
                else
                {
                    double cutScore = bugs * 5 + code_smells * 2 + security_rating * 2.5 + vulnerabilities * 5;
                    if (comment_lines_density.HasValue && comment_lines_density < 7)  //7.0%的注释为一个及格线,大于7%不扣分
                    {
                        cutScore += (7 - comment_lines_density.Value);
                    }
                    if (duplicated_lines_density > 5)
                    {
                        cutScore += (comment_lines_density.Value - 5);  //重复率每增加1百分比,扣1分
                    }
                    this.Score = 100 - (float)cutScore;
                    this.Pass = this.Score >= minScore;
                    if (!this.Pass) 
                        this.Msg = "该代码综合质量评分过低,需改进后方可准入部署环境";
                    else
                        this.Msg = "代码综合质量分为:" + this.Score + ",允许部署";
                }
            }
    
            private float GetMetricValue(string key)
            {
                var item = this.measures.FirstOrDefault(p => p.metric == key);
                if (item == null) return 0f;
                return ConvertHelper.Tofloat(item.value, 0f);
            }
    
            private float? GetMetricValue2(string key)
            {
                var item = this.measures.FirstOrDefault(p => p.metric == key);
                if (item == null) return default(float?);
                return ConvertHelper.Tofloat2(item.value);
            }
        }
    

    部署接口到k8s,然后验证接口返回

    {
        "code_smells": 3,
        "bugs": 0,
        "coverage": 0,
        "duplicated_lines_density": 0,
        "ncloc": 138, 
        "security_rating": 1,
        "vulnerabilities": 0,
        "reliability_rating": 0,
        "comment_lines_density": 2.8,
        "score": 87.3,
        "pass": true,
        "msg": "代码综合质量分为:87.3,允许部署"
    }
    

    步骤4:Jenkins集成质量检查Api控制发布流程
    在昨天我们Jenkins执行扫描完毕环节后添加发下脚本:

    //说明:sonarCheckUrl是我们上面用.net core实现的API,地址:http://xxxxx/checkApi/Sonar/Scan?projectKey=$sonarKey
    
               def response = httpRequest acceptType: 'APPLICATION_JSON', contentType: 'APPLICATION_JSON', httpMode: 'GET',  url: "$sonarCheckUrl?projectKey=$sonarKey"       
               def responseBodyJson = readJSON text: response.content 
               if (responseBodyJson.pass) {
                   println("#############################################代码质量检测通过,综合得分:"+responseBodyJson.score+ "##################################################")
               } else {                     
                   println("#############################################代码质量检测不通过,综合得分:"+responseBodyJson.score+"原因" + responseBodyJson.msg+ ",流水线退出##################################################")
                   sh "exit 1"
               }
    

    修改完毕后,我们重新执行发布过程,发布结果输出如下:

    这里是刚扫描结束
    INFO: Total time: 5.882s
    INFO: Final Memory: 12M/44M
    INFO: ------------------------------------------------------------------------
    The SonarQube Scanner has finished
    12:40:54.591  Post-processing succeeded. 
    [Pipeline] echo #############################################代码质量检查完毕################################################## 
    [Pipeline] echo #############################################开始请求质量检查结果##################################################
    [Pipeline] httpRequest HttpMethod: GET
    URL: [http://api.xxxx.cn/checkApi//onar/Scan?projectKey=netCore01](http://api.xxxx.cn/checkApi//Sonar/Scan?projectKey=netCore01)
    Content-Type: application/json
    Accept: application/json
    Sending request to url: [http://api.xxxx.cn/checkApi/Sonar/Scan?projectKey=netCore01](http://api.xxx.cn/checkApi/Sonar/Scan?projectKey=netCore01)
    Response Code: HTTP/1.1 200 OK
    Success code from [100‥399] 
    [Pipeline] echo sonar Response:Status: 200 
    [Pipeline] echo response.content:{"code_smells":3,"bugs":0,"coverage":0,"duplicated_lines_density":0,"ncloc":138,"security_rating":1,"vulnerabilities":0,"reliability_rating":0,"comment_lines_density":2.8,"score":87.3,"pass":true,"msg":"代码综合质量分为:87.3,允许部署"} 
    [Pipeline] readJSON
    [Pipeline] echo 
    #############################################代码质量检测通过,综合得分:87.3################################################## 
    [Pipeline] } 
    [Pipeline] // withEnv 
    

    3. 小结

    本文通过对Sonar Api扩展及集成,实现了自定义的代码质量控制模型及策略,这种方案相对更灵活一些。
    您也可以使用sonarqube中自带的【质量阈】,通过界面配置不同的策略进行控制的方式。
    另外API获取数据的方案您也可以改为从sonar数据库中查询数据的方式获取,但该方式容易因为版本升级出现不兼容问题。

    3.1 Sonar质量阈方案

    Sonarqube质量阈控制
    3.2 SQL查询数据方案
    先查出项目uuid:
    SELECT project_uuid,kee FROM projects where kee='projName'
    --BUG
    SELECT * FROM issues WHERE project_uuid='项目UUID' and issue_type=2
    --漏洞
    SELECT * FROM issues WHERE project_uuid='项目UUID' and issue_type=3
    --坏味道
    SELECT * FROM issues WHERE project_uuid='项目UUID' and issue_type=1
    若要分级别获取,可以再加上条件
     severity =
     ['BLOCKER','CRITICAL',"MAJOR",'MINOR','INFO']
    对应含义:阻断/严重/主要/次要/提示
    

    相关文章

      网友评论

          本文标题:基于Sonarqube Api---Jenkins集成发布质量控

          本文链接:https://www.haomeiwen.com/subject/jnjhwhtx.html