blob: 88fd5c791eeaf675a855cfc7fced7e33b7d7919a [file] [log] [blame]
Jakub Josef79ecec32017-02-17 14:36:28 +01001package com.mirantis.mk
2
Jakub Josefb41c8d52017-03-24 13:52:24 +01003import static groovy.json.JsonOutput.prettyPrint
4import static groovy.json.JsonOutput.toJson
Jakub Josefb7ab8472017-04-05 14:56:53 +02005import groovy.json.JsonSlurperClassic
Jakub Josef79ecec32017-02-17 14:36:28 +01006/**
7 *
8 * Common functions
9 *
10 */
11
12/**
13 * Generate current timestamp
14 *
15 * @param format Defaults to yyyyMMddHHmmss
16 */
17def getDatetime(format="yyyyMMddHHmmss") {
18 def now = new Date();
19 return now.format(format, TimeZone.getTimeZone('UTC'));
20}
21
22/**
Jakub Josef79ecec32017-02-17 14:36:28 +010023 * Return workspace.
24 * Currently implemented by calling pwd so it won't return relevant result in
25 * dir context
26 */
27def getWorkspace() {
28 def workspace = sh script: 'pwd', returnStdout: true
29 workspace = workspace.trim()
30 return workspace
31}
32
33/**
Filip Pytloun81c864d2017-03-21 15:19:30 +010034 * Get UID of jenkins user.
35 * Must be run from context of node
36 */
37def getJenkinsUid() {
38 return sh (
39 script: 'id -u',
40 returnStdout: true
41 ).trim()
42}
43
44/**
45 * Get GID of jenkins user.
46 * Must be run from context of node
47 */
48def getJenkinsGid() {
49 return sh (
50 script: 'id -g',
51 returnStdout: true
52 ).trim()
53}
54
55/**
Jakub Josef79ecec32017-02-17 14:36:28 +010056 * Get credentials from store
57 *
58 * @param id Credentials name
59 */
Jakub Josef3d9d9ab2017-03-14 15:09:03 +010060def getCredentials(id, cred_type = "username_password") {
61 def credClass;
62 if(cred_type == "username_password"){
63 credClass = com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials.class
64 }else if(cred_type == "key"){
65 credClass = com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey.class
66 }
Jakub Josef79ecec32017-02-17 14:36:28 +010067 def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
Jakub Josef3d9d9ab2017-03-14 15:09:03 +010068 credClass,
Jakub Josef79ecec32017-02-17 14:36:28 +010069 jenkins.model.Jenkins.instance
70 )
71
72 for (Iterator<String> credsIter = creds.iterator(); credsIter.hasNext();) {
73 c = credsIter.next();
74 if ( c.id == id ) {
75 return c;
76 }
77 }
78
79 throw new Exception("Could not find credentials for ID ${id}")
80}
81
82/**
83 * Abort build, wait for some time and ensure we will terminate
84 */
85def abortBuild() {
86 currentBuild.build().doStop()
87 sleep(180)
88 // just to be sure we will terminate
89 throw new InterruptedException()
90}
91
92/**
Jakub Josefb41c8d52017-03-24 13:52:24 +010093 * Return pretty-printed string representation of given item
94 * @param item item to be pretty-printed (list, map, whatever)
95 * @return pretty-printed string
96 */
97def prettyPrint(item){
98 return prettyPrint(toJson(item)).replace('\\n', System.getProperty('line.separator'))
99}
100
101/**
Jakub Josef79ecec32017-02-17 14:36:28 +0100102 * Print informational message
103 *
104 * @param msg
105 * @param color Colorful output or not
106 */
107def infoMsg(msg, color = true) {
108 printMsg(msg, "cyan")
109}
110
111/**
112 * Print error message
113 *
114 * @param msg
115 * @param color Colorful output or not
116 */
117def errorMsg(msg, color = true) {
118 printMsg(msg, "red")
119}
120
121/**
122 * Print success message
123 *
124 * @param msg
125 * @param color Colorful output or not
126 */
127def successMsg(msg, color = true) {
128 printMsg(msg, "green")
129}
130
131/**
132 * Print warning message
133 *
134 * @param msg
135 * @param color Colorful output or not
136 */
137def warningMsg(msg, color = true) {
Jakub Josef0e7bd632017-03-16 16:25:05 +0100138 printMsg(msg, "yellow")
Jakub Josef79ecec32017-02-17 14:36:28 +0100139}
140
141/**
Jakub Josef952ae0b2017-03-14 19:04:21 +0100142 * Print debug message, this message will show only if DEBUG global variable is present
143 * @param msg
144 * @param color Colorful output or not
145 */
146def debugMsg(msg, color = true){
Jakub Josef9a836ac2017-04-24 12:26:02 +0200147 // if debug property exists on env, debug is enabled
Jakub Josef66976f62017-04-24 16:32:23 +0200148 if(env.getEnvironment().containsKey('DEBUG') && env['DEBUG'] == "true"){
Jakub Josef74b34692017-03-15 12:10:57 +0100149 printMsg("[DEBUG] ${msg}", "red")
Jakub Josef952ae0b2017-03-14 19:04:21 +0100150 }
151}
152
153/**
Jakub Josef79ecec32017-02-17 14:36:28 +0100154 * Print message
155 *
156 * @param msg Message to be printed
157 * @param level Level of message (default INFO)
158 * @param color Color to use for output or false (default)
159 */
160def printMsg(msg, color = false) {
161 colors = [
162 'red' : '\u001B[31m',
163 'black' : '\u001B[30m',
164 'green' : '\u001B[32m',
165 'yellow': '\u001B[33m',
166 'blue' : '\u001B[34m',
167 'purple': '\u001B[35m',
168 'cyan' : '\u001B[36m',
169 'white' : '\u001B[37m',
170 'reset' : '\u001B[0m'
171 ]
172 if (color != false) {
173 wrap([$class: 'AnsiColorBuildWrapper']) {
174 print "${colors[color]}${msg}${colors.reset}"
175 }
176 } else {
177 print "[${level}] ${msg}"
178 }
179}
180
181/**
182 * Traverse directory structure and return list of files
183 *
184 * @param path Path to search
185 * @param type Type of files to search (groovy.io.FileType.FILES)
186 */
187@NonCPS
188def getFiles(path, type=groovy.io.FileType.FILES) {
189 files = []
190 new File(path).eachFile(type) {
191 files[] = it
192 }
193 return files
194}
195
196/**
197 * Helper method to convert map into form of list of [key,value] to avoid
198 * unserializable exceptions
199 *
200 * @param m Map
201 */
202@NonCPS
203def entries(m) {
204 m.collect {k, v -> [k, v]}
205}
206
207/**
208 * Opposite of build-in parallel, run map of steps in serial
209 *
210 * @param steps Map of String<name>: CPSClosure2<step>
211 */
212def serial(steps) {
213 stepsArray = entries(steps)
214 for (i=0; i < stepsArray.size; i++) {
Jakub Josefd31de302017-05-15 13:59:18 +0200215 def step = stepsArray[i]
216 dummySteps = [:]
217 if(step[1] instanceof Iterable){
218 for(j=0;j < step[1].size; i++){
219 dummySteps.put(step[0],step[1][j])
220 }
221 }else{
222 dummySteps.put(step[0], step[1])
223 }
Jakub Josef79ecec32017-02-17 14:36:28 +0100224 parallel dummySteps
225 }
226}
227
228/**
229 * Get password credentials from store
230 *
231 * @param id Credentials name
232 */
233def getPasswordCredentials(id) {
234 def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
235 com.cloudbees.plugins.credentials.common.StandardUsernamePasswordCredentials.class,
236 jenkins.model.Jenkins.instance
237 )
238
239 for (Iterator<String> credsIter = creds.iterator(); credsIter.hasNext();) {
240 c = credsIter.next();
241 if ( c.id == id ) {
242 return c;
243 }
244 }
245
246 throw new Exception("Could not find credentials for ID ${id}")
247}
248
249/**
250 * Get SSH credentials from store
251 *
252 * @param id Credentials name
253 */
254def getSshCredentials(id) {
255 def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
256 com.cloudbees.plugins.credentials.common.StandardUsernameCredentials.class,
257 jenkins.model.Jenkins.instance
258 )
259
260 for (Iterator<String> credsIter = creds.iterator(); credsIter.hasNext();) {
261 c = credsIter.next();
262 if ( c.id == id ) {
263 return c;
264 }
265 }
266
267 throw new Exception("Could not find credentials for ID ${id}")
268}
Jakub Josef79ecec32017-02-17 14:36:28 +0100269
270/**
271 * Tests Jenkins instance for existence of plugin with given name
272 * @param pluginName plugin short name to test
273 * @return boolean result
274 */
275@NonCPS
276def jenkinsHasPlugin(pluginName){
277 return Jenkins.instance.pluginManager.plugins.collect{p -> p.shortName}.contains(pluginName)
278}
279
280@NonCPS
281def _needNotification(notificatedTypes, buildStatus, jobName) {
282 if(notificatedTypes && notificatedTypes.contains("onchange")){
283 if(jobName){
284 def job = Jenkins.instance.getItem(jobName)
285 def numbuilds = job.builds.size()
286 if (numbuilds > 0){
287 //actual build is first for some reasons, so last finished build is second
288 def lastBuild = job.builds[1]
289 if(lastBuild){
290 if(lastBuild.result.toString().toLowerCase().equals(buildStatus)){
291 println("Build status didn't changed since last build, not sending notifications")
292 return false;
293 }
294 }
295 }
296 }
297 }else if(!notificatedTypes.contains(buildStatus)){
298 return false;
299 }
300 return true;
301}
302
303/**
304 * Send notification to all enabled notifications services
305 * @param buildStatus message type (success, warning, error), null means SUCCESSFUL
306 * @param msgText message text
307 * @param enabledNotifications list of enabled notification types, types: slack, hipchat, email, default empty
308 * @param notificatedTypes types of notifications will be sent, default onchange - notificate if current build result not equal last result;
309 * otherwise use - ["success","unstable","failed"]
310 * @param jobName optional job name param, if empty env.JOB_NAME will be used
311 * @param buildNumber build number param, if empty env.JOB_NAME will be used
312 * @param buildUrl build url param, if empty env.JOB_NAME will be used
313 * @param mailFrom mail FROM param, if empty "jenkins" will be used, it's mandatory for sending email notifications
314 * @param mailTo mail TO param, it's mandatory for sending email notifications
315 */
316def sendNotification(buildStatus, msgText="", enabledNotifications = [], notificatedTypes=["onchange"], jobName=null, buildNumber=null, buildUrl=null, mailFrom="jenkins", mailTo=null){
317 // Default values
318 def colorName = 'blue'
319 def colorCode = '#0000FF'
320 def buildStatusParam = buildStatus != null && buildStatus != "" ? buildStatus : "SUCCESS"
321 def jobNameParam = jobName != null && jobName != "" ? jobName : env.JOB_NAME
322 def buildNumberParam = buildNumber != null && buildNumber != "" ? buildNumber : env.BUILD_NUMBER
323 def buildUrlParam = buildUrl != null && buildUrl != "" ? buildUrl : env.BUILD_URL
324 def subject = "${buildStatusParam}: Job '${jobNameParam} [${buildNumberParam}]'"
325 def summary = "${subject} (${buildUrlParam})"
326
327 if(msgText != null && msgText != ""){
328 summary+="\n${msgText}"
329 }
330 if(buildStatusParam.toLowerCase().equals("success")){
331 colorCode = "#00FF00"
332 colorName = "green"
333 }else if(buildStatusParam.toLowerCase().equals("unstable")){
334 colorCode = "#FFFF00"
335 colorName = "yellow"
336 }else if(buildStatusParam.toLowerCase().equals("failure")){
337 colorCode = "#FF0000"
338 colorName = "red"
339 }
340 if(_needNotification(notificatedTypes, buildStatusParam.toLowerCase(), jobNameParam)){
341 if(enabledNotifications.contains("slack") && jenkinsHasPlugin("slack")){
342 try{
343 slackSend color: colorCode, message: summary
344 }catch(Exception e){
345 println("Calling slack plugin failed")
346 e.printStackTrace()
347 }
348 }
349 if(enabledNotifications.contains("hipchat") && jenkinsHasPlugin("hipchat")){
350 try{
351 hipchatSend color: colorName.toUpperCase(), message: summary
352 }catch(Exception e){
353 println("Calling hipchat plugin failed")
354 e.printStackTrace()
355 }
356 }
357 if(enabledNotifications.contains("email") && mailTo != null && mailTo != "" && mailFrom != null && mailFrom != ""){
358 try{
359 mail body: summary, from: mailFrom, subject: subject, to: mailTo
360 }catch(Exception e){
361 println("Sending mail plugin failed")
362 e.printStackTrace()
363 }
364 }
365 }
Filip Pytloun49d66302017-03-06 10:26:22 +0100366}
chnyda4e5ac792017-03-14 15:24:18 +0100367
368/**
369 * Execute linux command and catch nth element
370 * @param cmd command to execute
371 * @param index index to retrieve
372 * @return index-th element
373 */
374
375def cutOrDie(cmd, index)
376{
377 def common = new com.mirantis.mk.Common()
378 def output
379 try {
380 output = sh(script: cmd, returnStdout: true)
381 def result = output.tokenize(" ")[index]
382 return result;
383 } catch (Exception e) {
384 common.errorMsg("Failed to execute cmd: ${cmd}\n output: ${output}")
385 }
Filip Pytloun81c864d2017-03-21 15:19:30 +0100386}
Tomáš Kukrál767dd732017-03-23 10:38:59 +0100387
388/**
389 * Check variable contains keyword
390 * @param variable keywork is searched (contains) here
391 * @param keyword string to look for
392 * @return True if variable contains keyword (case insensitive), False if do not contains or any of input isn't a string
393 */
394
395def checkContains(variable, keyword) {
Jakub Josef7a8dea22017-03-23 19:51:32 +0100396 if(env.getEnvironment().containsKey(variable)){
397 return env[variable] && env[variable].toLowerCase().contains(keyword.toLowerCase())
Tomáš Kukrál767dd732017-03-23 10:38:59 +0100398 } else {
Tomáš Kukrálc76c1e02017-03-23 19:06:59 +0100399 return false
Tomáš Kukrál767dd732017-03-23 10:38:59 +0100400 }
401}
Jakub Josefa877db52017-04-05 14:22:30 +0200402
403/**
404 * Parse JSON string to hashmap
405 * @param jsonString input JSON string
406 * @return created hashmap
407 */
408def parseJSON(jsonString){
409 def m = [:]
Jakub Josefb7ab8472017-04-05 14:56:53 +0200410 def lazyMap = new JsonSlurperClassic().parseText(jsonString)
Jakub Josefa877db52017-04-05 14:22:30 +0200411 m.putAll(lazyMap)
412 return m
413}
Jakub Josefed239cd2017-05-09 15:27:33 +0200414
415/**
416 * Test pipeline input parameter existence and validity (not null and not empty string)
417 * @param paramName input parameter name (usually uppercase)
418 */
419def validInputParam(paramName){
420 return env.getEnvironment().containsKey(paramName) && env[paramName] != null && env[paramName] != ""
421}