blob: c993999a3f6952309b94690b1709fe39ff82c81d [file] [log] [blame]
Jakub Josef4edd7432017-05-10 17:58:56 +02001package com.mirantis.mk
Jakub Josef93f08e22017-06-05 19:14:53 +02002import com.cloudbees.groovy.cps.NonCPS
Mykyta Karpin158e44d2024-10-10 14:54:14 +02003import org.jenkins.plugins.lockableresources.LockableResourcesManager as LRM
Jakub Josef4edd7432017-05-10 17:58:56 +02004
5/**
6 *
7 * Jenkins common functions
8 *
9 */
10
11/**
Kirill Mashchenko43001092018-12-25 05:28:53 +040012 * Returns a list of groups which user belongs
13 * @param username String
14 * @return list of groups [String]
Jakub Josefd44b6972018-01-23 17:55:57 +010015 */
Kirill Mashchenko43001092018-12-25 05:28:53 +040016def userGroups(username) {
17 res = []
18 def authorities = Jenkins.instance.securityRealm.loadUserByUsername(username).getAuthorities()
19 authorities.each {
20 res.add(it.toString())
Jakub Josefd44b6972018-01-23 17:55:57 +010021 }
Kirill Mashchenko43001092018-12-25 05:28:53 +040022 return res
23}
24
25/**
26 * Check if user belongs to group
27 * @param username String
28 * @param group String
29 * @return boolean result
30 */
31def userInGroup(username, group) {
32 def authorities = userGroups(username)
33 return authorities.any{it==group}
34}
35
36/**
37 * Check if user belongs to at least one of given groups
38 * @param username String
39 * @param groups [String]
40 * @return boolean result
41 */
42def userInGroups(username, groups) {
43 return groups.any{userInGroup(username, it)}
44}
45
46/**
47 * Returns current username from build
48 * @return username String
49 */
50def currentUsername() {
51 username = ''
52 wrap([$class: 'BuildUser']) {
Maxim Rasskazov2b7c3be2019-06-21 14:50:02 +040053 username = env.BUILD_USER_ID ?: 'jenkins'
Kirill Mashchenko43001092018-12-25 05:28:53 +040054 }
55 if (username) {
56 return username
57 } else {
58 throw new Exception('cant get current username')
59 }
60}
61
62/**
63 * Check if current user belongs to at least one of given groups
64 * @param groups [String]
65 * @return boolean result
66 */
67def currentUserInGroups(groups) {
68 username = currentUsername()
69 return userInGroups(username, groups)
70}
71
72/**
73 * Check if current user belongs to group
74 * @param group String
75 * @return boolean result
76 */
77def currentUserInGroup(group) {
78 username = currentUsername()
79 return userInGroup(username, group)
Jakub Josefd44b6972018-01-23 17:55:57 +010080}
81
82/**
Jakub Josef4edd7432017-05-10 17:58:56 +020083 * Get Jenkins job running builds
84 * @param jobName job name
85 * @return list of running builds
86 */
Jakub Joseffbe8c7c2017-05-11 13:35:11 +020087@NonCPS
Jakub Josef4edd7432017-05-10 17:58:56 +020088def getJobRunningBuilds(jobName){
Kirill Mashchenko43001092018-12-25 05:28:53 +040089 def job = Jenkins.instance.items.find{it -> it.name.equals(jobName)}
90 if(job){
91 return job.builds.findAll{build -> build.isBuilding()}
92 }
93 return []
Jakub Josef93f08e22017-06-05 19:14:53 +020094}
95
96@NonCPS
97def getRunningBuilds(job){
Kirill Mashchenko43001092018-12-25 05:28:53 +040098 return job.builds.findAll{build -> build.isBuilding()}
Jakub Josef93f08e22017-06-05 19:14:53 +020099}
100
101@NonCPS
102def killStuckBuilds(maxSeconds, job){
Kirill Mashchenko43001092018-12-25 05:28:53 +0400103 def common = new com.mirantis.mk.Common()
104 def result = true
105 def runningBuilds = getRunningBuilds(job)
106 def jobName = job.name
107 for(int j=0; j < runningBuilds.size(); j++){
108 int durationInSeconds = (System.currentTimeMillis() - runningBuilds[j].getTimeInMillis())/1000.0
109 if(durationInSeconds > maxSeconds){
110 result = false
111 def buildId = runningBuilds[j].id
112 common.infoMsg("Aborting ${jobName}-${buildId} which is running for ${durationInSeconds}s")
113 try{
114 runningBuilds[j].finish(hudson.model.Result.ABORTED, new java.io.IOException("Aborting build by long running jobs killer"));
115 result = true
116 }catch(e){
117 common.errorMsg("Error occured during aborting build: Exception: ${e}")
118 }
119 }
Jakub Josef93f08e22017-06-05 19:14:53 +0200120 }
Kirill Mashchenko43001092018-12-25 05:28:53 +0400121 return result
Jakub Josefd44b6972018-01-23 17:55:57 +0100122}
Richard Felkl838892f2018-06-12 17:58:20 +0200123
124/**
125 * Get Jenkins job object
126 * @param jobName job name
127 * @return job object that matches jobName
128 */
Denis Egorenkob1a40b62019-01-11 18:04:57 +0400129def getJobByName(jobName, regexp=false){
Richard Felkl838892f2018-06-12 17:58:20 +0200130 for(item in Hudson.instance.items) {
Denis Egorenkob1a40b62019-01-11 18:04:57 +0400131 if (regexp && item.name ==~ jobName || item.name == jobName) {
Richard Felkl838892f2018-06-12 17:58:20 +0200132 return item
133 }
134 }
135}
136
137/**
138 * Get Jenkins job parameters
139 * @param jobName job name
140 * @return HashMap with parameter names as keys and their values as values
141 */
142def getJobParameters(jobName){
143 def job = getJobByName(jobName)
144 def prop = job.getProperty(ParametersDefinitionProperty.class)
145 def params = new java.util.HashMap<String,String>()
146 if(prop != null) {
147 for(param in prop.getParameterDefinitions()) {
148 params.put(param.name, param.defaultValue)
149 }
150 }
151 return params
152}
Denis Egorenko46ff1382018-12-14 15:46:36 +0400153
154/**
155 * Get list of causes actions for given build
156 *
157 * @param build Job build object (like, currentBuild.rawBuild)
158 * @return list of causes actions for given build
159 */
160@NonCPS
161def getBuildCauseActions(build) {
Denis Egorenko270c5302019-01-10 20:53:01 +0400162 for(action in build.actions) {
163 if (action instanceof hudson.model.CauseAction) {
164 return action.causes
165 }
Denis Egorenko46ff1382018-12-14 15:46:36 +0400166 }
Denis Egorenko270c5302019-01-10 20:53:01 +0400167 return []
Denis Egorenko46ff1382018-12-14 15:46:36 +0400168}
169
170/**
171 * Get list of builds, triggered by Gerrit with given build
172 * @param build Job build object (like, currentBuild.rawBuild)
173 * @return list of builds with names and numbers
174 */
175@NonCPS
176def getGerritBuildContext(build) {
177 def causes = getBuildCauseActions(build)
Denis Egorenko270c5302019-01-10 20:53:01 +0400178 for(cause in causes) {
179 if (cause instanceof com.sonyericsson.hudson.plugins.gerrit.trigger.hudsontrigger.GerritCause) {
180 return cause.context.getOtherBuilds()
181 }
Denis Egorenko46ff1382018-12-14 15:46:36 +0400182 }
Denis Egorenko270c5302019-01-10 20:53:01 +0400183 return []
Denis Egorenko46ff1382018-12-14 15:46:36 +0400184}
185
186/**
187 * Wait for other jobs
188 * @param config config parameter:
189 * builds - List of job build objects, which should be checked
190 * checkBuilds - List of job names or regexps, which should be used to check provided builds list
191 * regexp - Wheither to use regexp or simple string matching
192 */
Denis Egorenko270c5302019-01-10 20:53:01 +0400193@NonCPS
Denis Egorenko46ff1382018-12-14 15:46:36 +0400194def waitForOtherBuilds(LinkedHashMap config){
Denis Egorenko270c5302019-01-10 20:53:01 +0400195 def context = config.get('context', 'gerrit')
196 def builds = []
197 if (context == 'gerrit') {
198 builds = getGerritBuildContext(currentBuild.rawBuild)
199 } else if (context == 'custom') {
200 builds = config.get('builds')
201 }
Denis Egorenko46ff1382018-12-14 15:46:36 +0400202 def checkBuilds = config.get('checkBuilds')
203 def regexp = config.get('regexp', false)
Denis Egorenko270c5302019-01-10 20:53:01 +0400204
Denis Egorenko46ff1382018-12-14 15:46:36 +0400205 def waitForBuilds = builds.findAll { build ->
206 def jobName = build.fullDisplayName.tokenize(' ')[0]
207 if (regexp) {
208 checkBuilds.find { jobName ==~ it }
209 } else {
210 jobName in checkBuilds
211 }
212 }
Denis Egorenko270c5302019-01-10 20:53:01 +0400213
214 def buildsMap = []
Denis Egorenko46ff1382018-12-14 15:46:36 +0400215 if (waitForBuilds) {
216 def waiting = true
Denis Egorenko270c5302019-01-10 20:53:01 +0400217 print "\u001B[36mWaiting for next jobs: ${waitForBuilds}\u001B[0m"
Denis Egorenko46ff1382018-12-14 15:46:36 +0400218 while(waiting) {
219 waiting = false
220 waitForBuilds.each { job ->
221 if (job.inProgress) {
222 waiting = true
Denis Egorenko270c5302019-01-10 20:53:01 +0400223 } else {
224 buildInfo = [
225 'jobName': job.fullDisplayName.tokenize(' ')[0],
226 'jobNumber': job.number,
227 ]
228 buildsMap.add(buildInfo)
Denis Egorenko46ff1382018-12-14 15:46:36 +0400229 }
230 }
231 }
232 }
Denis Egorenko270c5302019-01-10 20:53:01 +0400233 return buildsMap
Denis Egorenko46ff1382018-12-14 15:46:36 +0400234}
azvyagintseveb817352019-11-08 13:30:17 +0200235
236/**
237 * Check dependency jobs passed successfully
238
Владислав Наумов90caa882020-08-12 17:20:12 +0200239 * @param block (bool) Block child jobs in case of parent dependencies failed
240 * @param allowNotBuilt (bool) Approve not_built status of the dependency job
241 * @return (map)[
242 * status: (bool) True if there are no failed dependencies
243 * log: (string) Verbose description
244 * ]
azvyagintseveb817352019-11-08 13:30:17 +0200245 */
Владислав Наумов90caa882020-08-12 17:20:12 +0200246def checkDependencyJobs(block = true, allowNotBuilt = false) {
azvyagintseveb817352019-11-08 13:30:17 +0200247 def common = new com.mirantis.mk.Common()
Владислав Наумов90caa882020-08-12 17:20:12 +0200248 def acceptedStatuses = ['SUCCESS']
249 if (allowNotBuilt) {
250 acceptedStatuses.add('NOT_BUILT')
251 }
252
azvyagintseveb817352019-11-08 13:30:17 +0200253 depList = []
254 if (env.TRIGGER_DEPENDENCY_KEYS){
255 common.infoMsg('Job may depends on parent jobs, check if dependency jobs exist...')
256 depKeys = env.TRIGGER_DEPENDENCY_KEYS.toString()
257 depList = depKeys.split()
258 if (depList){
Владислав Наумов90caa882020-08-12 17:20:12 +0200259 common.infoMsg("Here is dependency jobs-list: ${depList} , accepted job statuses are: ${acceptedStatuses}")
azvyagintseveb817352019-11-08 13:30:17 +0200260 for (String item : depList) {
261 prjName = item.replaceAll('[^a-zA-Z0-9]+', '_')
262 triggerResult = 'TRIGGER_' + prjName.toUpperCase() + '_BUILD_RESULT'
263 triggerJobName = 'TRIGGER_' + prjName.toUpperCase() + '_BUILD_NAME'
264 triggerJobBuild = 'TRIGGER_' + prjName.toUpperCase() + '_BUILD_NUMBER'
Владислав Наумов90caa882020-08-12 17:20:12 +0200265 if (!acceptedStatuses.contains(env.getProperty(triggerResult))) {
azvyagintseveb817352019-11-08 13:30:17 +0200266 msg = "Dependency job ${env.getProperty(triggerJobName)} #${env.getProperty(triggerJobBuild)} is ${env.getProperty(triggerResult)}"
267 common.warningMsg(msg)
268 if (block){
269 currentBuild.result = 'NOT_BUILT'
270 currentBuild.description = msg
271 }
272 return [status: false, log: msg, jobs: depList]
273 }
274 }
275 }
276 } else {
277 common.infoMsg('There is no job-dependencies')
278 }
279 return [status: true, log: '', jobs: depList]
280}
Владислав Наумов34e9ed82021-02-02 17:04:56 +0100281
282/**
283 * Return jenkins infra metadata according to specified jenkins intstance
284
285 * @param jenkinsServerURL (string) URL to jenkins server in form: env.JENKINS_URL
286 * @return (map)[
287 * jenkins_service_user: (string) name of jenkins user needed for gerrit ops
288 * ]
289 */
290def getJenkinsInfraMetadata(jenkinsServerURL) {
291 def meta = [
292 jenkins_service_user: '',
293 ]
294
295 switch (jenkinsServerURL) {
296 case 'https://ci.mcp.mirantis.net/':
297 meta['jenkins_service_user'] = 'mcp-jenkins'
298 break
299 case 'https://mcc-ci.infra.mirantis.net/':
300 meta['jenkins_service_user'] = 'mcc-ci-jenkins'
301 break
302 default:
303 error("Failed to detect jenkins service user, supported jenkins platforms: 'https://ci.mcp.mirantis.net/' 'https://mcc-ci.infra.mirantis.net/'")
304 }
305
306 return meta
307}
Владислав Наумов468bc742021-04-08 14:23:43 +0200308
309/**
310 * Get list of all jenkins workers matched desired label
311 *
312 * @param labelString (string) desired worker label
313 * @return (list) all workers, currently matched label
314 */
315@NonCPS
316def getWorkers(String labelString = null) {
317 def workerLabel = hudson.model.labels.LabelAtom.get(labelString)
318 def workers = []
319 hudson.model.Hudson.instance.slaves.each {
320 if (it.getComputer().isOnline()) {
321 if (workerLabel) {
322 if (workerLabel in it.getAssignedLabels()) {
323 workers << it.name
324 }
325 } else {
326 // if labelString is null, getting all workers
327 workers << it.name
328 }
329 }
330 }
331 return workers
332}
Mykyta Karpin158e44d2024-10-10 14:54:14 +0200333
334/**
335 * Get deployment environment and related jenkins lock label and lock resource
336 *
337 * @param initialEnv (string) Name of initially requested environment e.g. imc-eu or auto
338 * @param namespace (string) Name of environment namespace e.g imc-oscore-team
339 * @param resources (int) Quantity of required lockable resources
340 * @param candidateEnvs (list) List of names of env candidates to choose between
341 * @return (list) List whith environment name, lock label and lock resource
342 */
343def getEnvWithLockableResources(initialEnv, namespace, resources = 1, candidateEnvs = ["imc-eu", "imc-us"]){
344 def common = new com.mirantis.mk.Common()
345 def lockResource = null
346 def env = initialEnv
347 def lockLabel = "${namespace}-${env}"
348 def lrm = LRM.get()
349 if (initialEnv == "auto"){
350 def freeResources = [:]
351 for (cEnv in candidateEnvs){
352 def label = "${namespace}-${cEnv}"
353 freeResources[label] = lrm.getFreeResourceAmount(label)
354 }
355 common.infoMsg("Detecting target environment from candidates ${freeResources}")
356 def max = 0
357 def keys = freeResources.keySet().toList()
358 Collections.shuffle(keys)
359 for (key in keys){
360 if (freeResources[key] >= max){
361 max = freeResources[key]
362 lockLabel = key
363 }
364 }
365 if (max < resources){
366 lockLabel = keys[0]
367 }
368 env = lockLabel.replaceAll("${namespace}-", "")
369 common.infoMsg("Detected target environment ${env} lock ${lockLabel}")
370 }
371 // If no label configured on existing resources, create random lockresource
372 if (! lrm.isValidLabel(lockLabel)){
373 common.infoMsg("Running without locking, lock label ${lockLabel} does not exist")
374 lockLabel = null
375 lockResource = UUID.randomUUID().toString()
376 }
377 return [env, lockLabel, lockResource]
378}