新增代码扫描自定义步骤
约 3496 字
预计阅读 7 分钟
Summary
- 新增环境无关的自定义步骤,以新增sonarqube代码扫描为例子
新增代码扫描自定义步骤
- 以新增sonarqube代码扫描为例子,让DORY支持在执行编译构建前先进行代码扫描
- 所谓环境无关是指不依赖于应用的发布环境,例如代码扫描,不同发布环境代码扫描的参数没有区别
- 所谓环境相关是指依赖于应用的发布环境,例如自动化接口测试,不同发布环境测试的参数会有所不同
任务演示视频
- 演示如何让DORY支持在编译构建前执行sonarqube代码扫描
部署sonarqube服务
- 把sonarqube部署到kubernetes中,sonarqube需要部署sonarqube-web和sonarqube-db两个服务
- 样例部署文件如下:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
|
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: sonarqube-db
name: sonarqube-db
namespace: dory
spec:
replicas: 1
selector:
matchLabels:
app: sonarqube-db
serviceName: sonarqube-db
template:
metadata:
labels:
app: sonarqube-db
spec:
containers:
- env:
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
- name: POSTGRES_USER
value: sonar
- name: POSTGRES_PASSWORD
value: xxxx
image: postgres:12.9-alpine
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 90
periodSeconds: 30
successThreshold: 1
tcpSocket:
port: 5432
timeoutSeconds: 1
name: sonarqube-db
ports:
- containerPort: 5432
protocol: TCP
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 15
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 5432
timeoutSeconds: 1
resources:
limits:
cpu: 500m
memory: 200Mi
requests:
cpu: 100m
memory: 100Mi
volumeMounts:
- mountPath: /etc/timezone
name: timezone
- mountPath: /etc/localtime
name: localtime
- mountPath: /var/lib/postgresql/data/pgdata
name: dory-pvc
subPath: sonarqube-db
imagePullSecrets:
- name: ${HARBOR_DOMAIN_NAME}
volumes:
- hostPath:
path: /etc/timezone
type: File
name: timezone
- hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
type: File
name: localtime
- name: dory-pvc
persistentVolumeClaim:
claimName: dory-pvc
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: sonarqube-web
name: sonarqube-web
namespace: dory
spec:
replicas: 1
selector:
matchLabels:
app: sonarqube-web
serviceName: sonarqube-web-headless
template:
metadata:
labels:
app: sonarqube-web
spec:
containers:
- env:
- name: SONAR_JDBC_USERNAME
value: sonar
- name: SONAR_JDBC_PASSWORD
value: xxxx
- name: SONAR_JDBC_URL
value: jdbc:postgresql://sonarqube-db:5432/sonar
- name: SONAR_CE_JAVAOPTS
value: -Xmx512m -Xms512m
image: sonarqube:9.3.0-community
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
initialDelaySeconds: 90
periodSeconds: 30
successThreshold: 1
tcpSocket:
port: 9000
timeoutSeconds: 1
name: sonarqube-web
ports:
- containerPort: 9000
protocol: TCP
readinessProbe:
failureThreshold: 3
initialDelaySeconds: 15
periodSeconds: 5
successThreshold: 1
tcpSocket:
port: 9000
timeoutSeconds: 1
resources:
limits:
cpu: "1"
memory: 2Gi
requests:
cpu: 200m
memory: 100Mi
volumeMounts:
- mountPath: /etc/timezone
name: timezone
- mountPath: /etc/localtime
name: localtime
- mountPath: /opt/sonarqube/extensions
name: dory-pvc
subPath: sonarqube-web/extensions
- mountPath: /opt/sonarqube/data
name: dory-pvc
subPath: sonarqube-web/data
- mountPath: /opt/sonarqube/logs
name: dory-pvc
subPath: sonarqube-web/logs
- mountPath: /opt/sonarqube/temp
name: dory-pvc
subPath: sonarqube-web/temp
imagePullSecrets:
- name: ${HARBOR_DOMAIN_NAME}
initContainers:
- command:
- sh
- -c
- until nc -z -w 5 -v sonarqube-db 5432; do echo waiting for sonarqube-db
ready; done;
image: busybox:1.32.0
imagePullPolicy: IfNotPresent
name: init-sonarqube-db
volumes:
- hostPath:
path: /etc/timezone
type: File
name: timezone
- hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
type: File
name: localtime
- name: dory-pvc
persistentVolumeClaim:
claimName: dory-pvc
---
apiVersion: v1
kind: Service
metadata:
labels:
app: sonarqube-db
name: sonarqube-db
namespace: dory
spec:
clusterIP: None
ports:
- name: port-5432
port: 5432
protocol: TCP
targetPort: 5432
selector:
app: sonarqube-db
---
apiVersion: v1
kind: Service
metadata:
labels:
app: sonarqube-web
name: sonarqube-web
namespace: dory
spec:
ports:
- name: port-9000
nodePort: 30009
port: 9000
protocol: TCP
targetPort: 9000
selector:
app: sonarqube-web
type: NodePort
---
apiVersion: v1
kind: Service
metadata:
labels:
app: sonarqube-web
name: sonarqube-web-headless
namespace: dory
spec:
clusterIP: None
ports:
- name: port-9000
port: 9000
protocol: TCP
targetPort: 9000
selector:
app: sonarqube-web
type: ClusterIP
|
1
2
|
# 等待sonarqube服务启动
kubectl -n dory get pods -w
|
- sonarqube启动后,默认的管理员账号密码为admin/admin
制作sonar-scanner镜像
DORY自定义步骤的输入参数与输出参数:
输入参数文件
: 输入参数文件
由DORY自动创建。DORY执行自定义步骤过程中会自动把运行以及步骤的执行参数(内置参数)以及用户输入的参数自动保存到步骤执行容器的输入参数文件
中,输入参数文件
默认保存在/tmp/dory-param-input.json
或者/tmp/dory-param-input.yaml
文件中(注意,输入参数文件路径可以配置,请根据实际配置进行相关设置)
输入参数文件
支持保存成json格式或者yaml格式,步骤执行容器中的代码可以读取输入参数文件作为输入参数,执行相关的步骤脚本
输出参数文件
: 输出参数文件
由步骤执行容器在步骤执行过程中的步骤脚本创建。DORY执行自定义步骤结束的时候,会读取步骤执行容器中的输出参数文件
,输出参数文件
默认保存在/tmp/dory-param-output.json
或者/tmp/dory-param-output.yaml
文件中(注意,输出参数文件路径可以配置,请根据实际配置进行相关设置)
输出参数文件
支持保存成json格式或者yaml格式,DORY在步骤执行结束后会读取输出参数文件
,用于控制步骤执行结果、输出文件以及需要保存到步骤执行记录中的所有参数
输出参数文件
的doryStepFail
参数: 用于控制步骤执行结果是否失败,假如设置为true,即使步骤执行过程没有错误,步骤也会被标记为失败
输出参数文件
的doryOutputFiles
参数: 用于控制步骤执行结果是否有文件需要输出,例如自动化测试的测试报告,如果需要保存出来,那么需要把测试报告的文件路径设置到doryOutputFiles
参数中
输出参数文件
的其他参数: 将会保存到步骤执行记录的输出参数中,用于后续执行结果检索用途
- 制作sonar-scanner镜像,DORY使用sonar-scanner镜像把代码推送到sonarqube进行扫描
1
2
|
# 拉取sonar-scanner镜像
docker pull sonarsource/sonar-scanner-cli:4.6
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# create_sonar_project_properties.py
import json
import os
print("# create sonar-project.properties")
f = open("/tmp/dory-param-input.json", "r")
json_str = f.read()
data = json.loads(json_str)
sonar_url = os.getenv("SONAR_URL")
sonar_token = os.getenv("SONAR_TOKEN")
sonar_extra_properties = "\n".join(data["sonarExtraProperties"])
project_name = "{0}_{1}".format(data["projectName"], data["moduleName"])
sonar_properties = """
sonar.host.url={0}
sonar.login={1}
sonar.projectKey={2}
sonar.projectName={3}
sonar.sources=.
{4}
""".format(sonar_url, sonar_token, project_name, project_name, sonar_extra_properties)
f = open("sonar-project.properties", "w")
f.write(sonar_properties)
f.close()
|
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
|
# get_scan_result.py
import requests
import json
import os
print("# get sonar scan result")
f = open("/tmp/dory-param-input.json", "r")
json_str = f.read()
data = json.loads(json_str)
sonar_url = os.getenv("SONAR_URL")
sonar_token = os.getenv("SONAR_TOKEN")
project_name = "{0}_{1}".format(data["projectName"], data["moduleName"])
url = "{0}api/qualitygates/project_status?projectKey={1}".format(sonar_url, project_name)
r = requests.get(url, auth=(sonar_token, ''))
sonar_output = r.text
sonar_data = json.loads(sonar_output)
status = sonar_data["projectStatus"]["status"]
print("# sonarqube scan status:", status)
if status != "OK":
sonar_data["doryStepFail"] = True
else:
sonar_data["doryStepFail"] = False
sonar_pretty = json.dumps(sonar_data, indent=4, sort_keys=True)
print("# sonarqube scan result")
print(sonar_pretty)
f = open("/tmp/dory-param-output.json", "w")
f.write(sonar_pretty)
f.close()
|
- sonar-scanner镜像的Dockerfile
1
2
3
4
5
6
7
8
9
|
FROM sonarsource/sonar-scanner-cli:4.6
LABEL maintainer="cookeem"
LABEL email="cookeem@qq.com"
LABEL version="4.6"
COPY create_sonar_project_properties.py /tmp/
COPY get_scan_result.py /tmp/
RUN chown -R 1000:1000 /tmp/
WORKDIR /tmp/
USER 1000:1000
|
1
2
|
docker build -t ${HARBOR_DOMAIN_NAME}/public/sonar-scanner-cli:4.6-dory .
docker push ${HARBOR_DOMAIN_NAME}/public/sonar-scanner-cli:4.6-dory
|
设置自定义步骤配置
- 为安全考虑设置sonarqube项目默认可见性为私有
- 设置地址: ${SONARQUBE_URL}/admin/projects_management
- 创建sonarqube管理员token,用于sonar-scanner访问sonarqube
- 设置地址: ${SONARQUBE_URL}/account/security/
- 在dory-dashboard的
管理控制台
- 自定义步骤
中新增代码扫描的自定义步骤配置
,样例如下图:
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
|
customStepName: scanCode
customStepActionDesc: sonarqube scan code
customStepDesc: sonarqube代码扫描
customStepUsage: 扫描代码仓库特定目录
customStepDockerConf:
dockerImage: public/sonar-scanner-cli:4.6-dory
dockerCommands:
- python3 /tmp/create_sonar_project_properties.py
- sonar-scanner
- python3 /tmp/get_scan_result.py
dockerRunAsRoot: false
dockerVolumes:
- '{{ $.path }}:/src/{{ $.moduleName }}'
dockerEnvs:
- SONAR_URL=http://sonarqube-web.dory:9000/
- SONAR_TOKEN=xxxx
dockerWorkDir: '/src/{{ $.moduleName }}'
paramInputFormat: json
paramOutputFormat: json
paramInputYamlDef: |
# 要扫描的代码目录
path: Codes/Backend/tp1-gin-demo
# sonarqube额外扫描参数
sonarExtraProperties:
- sonar.sourceEncoding=UTF-8
paramOutputYamlDef: |
# 以下为sonarqube接口返回的扫描结果格式
projectStatus:
status: "OK"
conditions: []
periods: []
ignoredConditions: false
period:
mode: PREVIOUS_VERSION
date: "2022-02-04T15:26:51+0800"
|
在项目定义中设置代码扫描的模块定义
- 在dory-dashboard的
项目定义
中新增scanCode
自定义步骤的自定义步骤模块定义
,样例如下图:
1
2
3
4
5
6
7
8
9
10
|
enableMode: ""
customStepModuleDefs:
- moduleName: tp1-gin-demo
manualEnable: true
paramInputYaml: |
# 要扫描的代码目录
path: Codes/Backend/tp1-gin-demo
# sonarqube额外扫描参数
sonarExtraProperties:
- sonar.sourceEncoding=UTF-8
|
在流水线定义中插入代码扫描步骤
- 在dory-dashboard的
项目定义
的流水线定义
中createRunFiles
步骤后边插入自定义步骤scanCode
,样例如下图:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
isAutoDetectBuild: false
isQueue: false
builds:
- name: tp1-gin-demo
run: true
- name: tp1-go-demo
run: false
- name: tp1-gradle-demo
run: false
- name: tp1-new-demo
run: false
- name: tp1-node-demo
run: false
- name: tp1-python-demo
run: false
- name: tp1-rust-demo
run: false
- name: tp1-spring-demo
run: false
pipelineStep:
gitPull:
timeout: 60
build:
enable: true
timeout: 0
retry: 0
packageImage:
enable: false
timeout: 0
retry: 0
syncImage:
enable: true
retry: 0
deploy:
enable: true
retry: 0
applyIngress:
enable: true
retry: 0
checkDeploy:
enable: true
ignoreError: false
retry: 0
checkQuota:
enable: true
retry: 0
customStepPhaseDefs:
scanCode:
enable: true
ignoreError: true
timeout: 0
retry: 0
customStepInsertDefs:
createRunFiles:
- scanCode
|
执行流水线,在编译构建前进行代码扫描
检查sonarqube代码扫描结果
- 打开sonarqube新增了
test-project1_tp1-gin-demo
项目,并可以查看详细扫描结果