blob: f436508e54908659f7718d30554435477cb2b926 [file] [log] [blame]
Sergey Kolekonovba203982016-12-21 18:32:17 +04001package com.mirantis.mk
2
3/**
4 *
5 * Artifactory functions
6 *
7 */
8
9/**
10 * Make generic call using Artifactory REST API and return parsed JSON
11 *
Oleksandr Hrabard3063462019-07-18 16:46:46 +030012 * @param art Artifactory connection object
13 * @param uri URI which will be appended to artifactory server base URL
Sergey Kolekonovba203982016-12-21 18:32:17 +040014 * @param method HTTP method to use (default GET)
15 * @param data JSON data to POST or PUT
16 * @param headers Map of additional request headers
Oleksandr Hrabard3063462019-07-18 16:46:46 +030017 * @param prefix Default prefix "/api"
Sergey Kolekonovba203982016-12-21 18:32:17 +040018 */
Oleksandr Hrabard3063462019-07-18 16:46:46 +030019<<<<<<< HEAD
Sergey Kolekonovba203982016-12-21 18:32:17 +040020def restCall(art, uri, method = 'GET', data = null, headers = [:]) {
Oleksandr Hrabar04049342019-07-18 11:05:22 +000021 def connection = new URL("${art.url}/api${uri}").openConnection()
Oleksandr Hrabard3063462019-07-18 16:46:46 +030022=======
23def restCall(art, uri, method = 'GET', data = null, headers = [:], prefix = '/api') {
24 def connection = new URL("${art.url}${prefix}${uri}").openConnection()
25>>>>>>> 32c5611... Edited methods restCall, publishArtifactoryHelmChart, deleteArtifactoryHelmChart and added restPut2 for Artifactory.groovy
Sergey Kolekonovba203982016-12-21 18:32:17 +040026 if (method != 'GET') {
27 connection.setRequestMethod(method)
28 }
29
30 connection.setRequestProperty('User-Agent', 'jenkins-groovy')
31 connection.setRequestProperty('Accept', 'application/json')
32 connection.setRequestProperty('Authorization', "Basic " +
33 "${art.creds.username}:${art.creds.password}".bytes.encodeBase64().toString())
34
35 for (header in headers) {
36 connection.setRequestProperty(header.key, header.value)
37 }
38
39 if (data) {
40 connection.setDoOutput(true)
41 if (data instanceof String) {
42 connection.setRequestProperty('Content-Type', 'application/json')
43 dataStr = data
44 } else if (data instanceof java.io.File) {
45 connection.setRequestProperty('Content-Type', 'application/octet-stream')
46 dataStr = data.bytes
47 } else if (data instanceof byte[]) {
48 connection.setRequestProperty('Content-Type', 'application/octet-stream')
49 dataStr = data
50 } else {
51 connection.setRequestProperty('Content-Type', 'application/json')
52 dataStr = new groovy.json.JsonBuilder(data).toString()
53 }
54 def out = new OutputStreamWriter(connection.outputStream)
55 out.write(dataStr)
56 out.close()
57 }
58
59 if ( connection.responseCode >= 200 && connection.responseCode < 300 ) {
60 res = connection.inputStream.text
61 try {
62 return new groovy.json.JsonSlurperClassic().parseText(res)
63 } catch (Exception e) {
64 return res
65 }
66 } else {
67 throw new Exception(connection.responseCode + ": " + connection.inputStream.text)
68 }
69}
70
71/**
72 * Make GET request using Artifactory REST API and return parsed JSON
73 *
74 * @param art Artifactory connection object
75 * @param uri URI which will be appended to artifactory server base URL
76 */
77def restGet(art, uri) {
78 return restCall(art, uri)
79}
80
81/**
82 * Make PUT request using Artifactory REST API and return parsed JSON
83 *
84 * @param art Artifactory connection object
85 * @param uri URI which will be appended to artifactory server base URL
86 * @param data JSON Data to PUT
87 */
88def restPut(art, uri, data = null) {
89 return restCall(art, uri, 'PUT', data, ['Accept': '*/*'])
90}
91
92/**
Oleksandr Hrabard3063462019-07-18 16:46:46 +030093 * Make PUT request using Artifactory REST API and return parsed JSON
94 *
95 * @param art Artifactory connection object
96 * @param uri URI which will be appended to artifactory server base URL
97 * @param data JSON Data to PUT
98 * @param prefix Default prefix "/api"
99 */
100def restPut2(art, uri, prefix, data = null) {
101 return restCall(art, uri, 'PUT', data, ['Accept': '*/*'], prefix)
102
103
104/**
Sergey Kolekonovba203982016-12-21 18:32:17 +0400105 * Make DELETE request using Artifactory REST API
106 *
107 * @param art Artifactory connection object
108 * @param uri URI which will be appended to artifactory server base URL
109 */
110def restDelete(art, uri) {
111 return restCall(art, uri, 'DELETE', null, ['Accept': '*/*'])
112}
113
114/**
115 * Make POST request using Artifactory REST API and return parsed JSON
116 *
117 * @param art Artifactory connection object
118 * @param uri URI which will be appended to artifactory server base URL
119 * @param data JSON Data to PUT
120 */
121def restPost(art, uri, data = null) {
122 return restCall(art, uri, 'POST', data, ['Accept': '*/*'])
123}
124
125/**
126 * Query artifacts by properties
127 *
128 * @param art Artifactory connection object
129 * @param properties String or list of properties in key=value format
130 * @param repo Optional repository to search in
131 */
132def findArtifactByProperties(art, properties, repo) {
133 query = parseProperties(properties)
134 if (repo) {
135 query = query + "&repos=${repo}"
136 }
137 res = restGet(art, "/search/prop?${query}")
138 return res.results
139}
140
141/**
142 * Parse properties string or map and return URL-encoded string
143 *
144 * @param properties string or key,value map
145 */
146def parseProperties(properties) {
147 if (properties instanceof String) {
148 return properties
149 } else {
150 props = []
151 for (e in properties) {
152 props.push("${e.key}=${e.value}")
153 }
154 props = props.join('|')
155 return props
156 }
157}
158
159/**
160 * Set single property or list of properties to existing artifact
161 *
162 * @param art Artifactory connection object
163 * @param name Name of artifact
164 * @param version Artifact's version, eg. Docker image tag
165 * @param properties String or list of properties in key=value format
166 * @param recursive Set properties recursively (default false)
167 */
168def setProperty(art, name, version, properties, recursive = 0) {
169 props = parseProperties(properties)
170 restPut(art, "/storage/${art.outRepo}/${name}/${version}?properties=${props}&recursive=${recursive}")
171}
172
173/**
174 * Artifactory connection and context parameters
175 *
176 * @param url Artifactory server URL
177 * @param dockerRegistryBase Base to docker registry
178 * @param dockerRegistrySSL Use https to access docker registry
179 * @param outRepo Output repository name used in context of this
180 * connection
181 * @param credentialsID ID of credentials store entry
Jakub Josef79ecec32017-02-17 14:36:28 +0100182 * @param serverName Artifactory server name (optional)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400183 */
Jakub Josef79ecec32017-02-17 14:36:28 +0100184def connection(url, dockerRegistryBase, dockerRegistrySsl, outRepo, credentialsId = "artifactory", serverName = null) {
Sergey Kolekonovba203982016-12-21 18:32:17 +0400185 params = [
186 "url": url,
187 "credentialsId": credentialsId,
188 "docker": [
189 "base": dockerRegistryBase,
190 "ssl": dockerRegistrySsl
191 ],
192 "outRepo": outRepo,
193 "creds": getCredentials(credentialsId)
194 ]
195
196 if (dockerRegistrySsl ?: false) {
197 params["docker"]["proto"] = "https"
198 } else {
199 params["docker"]["proto"] = "http"
200 }
Jakub Josef79ecec32017-02-17 14:36:28 +0100201
202 if (serverName ?: null) {
203 params['server'] = Artifactory.server(serverName)
204 }
205
Sergey Kolekonovba203982016-12-21 18:32:17 +0400206 params["docker"]["url"] = "${params.docker.proto}://${params.outRepo}.${params.docker.base}"
207
208 return params
209}
210
211/**
212 * Push docker image and set artifact properties
213 *
214 * @param art Artifactory connection object
215 * @param img Docker image object
216 * @param imgName Name of docker image
217 * @param properties Map of additional artifact properties
218 * @param timestamp Build timestamp
219 * @param latest Push latest tag if set to true (default true)
220 */
221def dockerPush(art, img, imgName, properties, timestamp, latest = true) {
222 docker.withRegistry(art.docker.url, art.credentialsId) {
223 img.push()
224 // Also mark latest image
225 img.push("latest")
226 }
227
228 properties["build.number"] = currentBuild.build().environment.BUILD_NUMBER
229 properties["build.name"] = currentBuild.build().environment.JOB_NAME
230 properties["timestamp"] = timestamp
231
232 /* Set artifact properties */
233 setProperty(
234 art,
235 imgName,
236 timestamp,
237 properties
238 )
239
240 // ..and the same for latest
241 if (latest == true) {
242 setProperty(
243 art,
244 imgName,
245 "latest",
246 properties
247 )
248 }
249}
250
251/**
252 * Promote docker image to another environment
253 *
254 * @param art Artifactory connection object
255 * @param imgName Name of docker image
256 * @param tag Tag to promote
257 * @param env Environment (repository suffix) to promote to
258 * @param keep Keep artifact in source repository (copy, default true)
259 * @param latest Push latest tag if set to true (default true)
260 */
261def dockerPromote(art, imgName, tag, env, keep = true, latest = true) {
262 /* XXX: promotion this way doesn't work
263 restPost(art, "/docker/${art.outRepo}/v2/promote", [
264 "targetRepo": "${art.outRepo}-${env}",
265 "dockerRepository": imgName,
266 "tag": tag,
267 "copy": keep ? true : false
268 ])
269 */
270
271 action = keep ? "copy" : "move"
272 restPost(art, "/${action}/${art.outRepo}/${imgName}/${tag}?to=${art.outRepo}-${env}/${imgName}/${tag}")
273 if (latest == true) {
274 dockerUrl = "${art.docker.proto}://${art.outRepo}-${env}.${art.docker.base}"
275 docker.withRegistry(dockerUrl, art.credentialsId) {
276 img = docker.image("${imgName}:$tag")
277 img.pull()
278 img.push("latest")
279 }
280 }
281}
282
283/**
284 * Set offline parameter to repositories
285 *
286 * @param art Artifactory connection object
287 * @param repos List of base repositories
288 * @param suffix Suffix to append to new repository names
289 */
290def setOffline(art, repos, suffix) {
291 for (repo in repos) {
292 repoName = "${repo}-${suffix}"
293 restPost(art, "/repositories/${repoName}", ['offline': true])
294 }
295 return
296}
297
298/**
299 * Create repositories based on timestamp or other suffix from already
300 * existing repository
301 *
302 * @param art Artifactory connection object
303 * @param repos List of base repositories
304 * @param suffix Suffix to append to new repository names
305 */
306def createRepos(art, repos, suffix) {
307 def created = []
308 for (repo in repos) {
309 repoNewName = "${repo}-${suffix}"
310 repoOrig = restGet(art, "/repositories/${repo}")
311 repoOrig.key = repoNewName
312 repoNew = restPut(art, "/repositories/${repoNewName}", repoOrig)
313 created.push(repoNewName)
314 }
315 return created
316}
317
318/**
319 * Delete repositories based on timestamp or other suffix
320 *
321 * @param art Artifactory connection object
322 * @param repos List of base repositories
323 * @param suffix Suffix to append to new repository names
324 */
325def deleteRepos(art, repos, suffix) {
326 def deleted = []
327 for (repo in repos) {
328 repoName = "${repo}-${suffix}"
329 restDelete(art, "/repositories/${repoName}")
330 deleted.push(repoName)
331 }
332 return deleted
333}
334
Jakub Josef79ecec32017-02-17 14:36:28 +0100335@NonCPS
336def convertProperties(properties) {
337 return properties.collect { k,v -> "$k=$v" }.join(';')
338}
339
Sergey Kolekonovba203982016-12-21 18:32:17 +0400340/**
341 * Upload debian package
342 *
343 * @param art Artifactory connection object
344 * @param file File path
345 * @param properties Map with additional artifact properties
346 * @param timestamp Image tag
347 */
Sergey Kolekonovba203982016-12-21 18:32:17 +0400348
Jakub Josef79ecec32017-02-17 14:36:28 +0100349def uploadDebian(art, file, properties, distribution, component, timestamp) {
350 def arch = file.split('_')[-1].split('\\.')[0]
Sergey Kolekonovba203982016-12-21 18:32:17 +0400351
352 /* Set artifact properties */
353 properties["build.number"] = currentBuild.build().environment.BUILD_NUMBER
354 properties["build.name"] = currentBuild.build().environment.JOB_NAME
355 properties["timestamp"] = timestamp
Jakub Josef79ecec32017-02-17 14:36:28 +0100356
357 properties["deb.distribution"] = distribution
358 properties["deb.component"] = component
359 properties["deb.architecture"] = arch
360 props = convertProperties(properties)
361
362 def uploadSpec = """{
363 "files": [
364 {
365 "pattern": "${file}",
366 "target": "${art.outRepo}",
367 "props": "${props}"
368 }
369 ]
370 }"""
371 art.server.upload(uploadSpec)
Sergey Kolekonovba203982016-12-21 18:32:17 +0400372}
373
374/**
375 * Build step to upload docker image. For use with eg. parallel
376 *
377 * @param art Artifactory connection object
378 * @param img Image name to push
379 * @param properties Map with additional artifact properties
380 * @param timestamp Image tag
381 */
382def uploadDockerImageStep(art, img, properties, timestamp) {
383 return {
384 println "Uploading artifact ${img} into ${art.outRepo}"
385 dockerPush(
386 art,
387 docker.image("${img}:${timestamp}"),
388 img,
389 properties,
390 timestamp
391 )
392 }
393}
394
395/**
396 * Build step to upload package. For use with eg. parallel
397 *
398 * @param art Artifactory connection object
399 * @param file File path
400 * @param properties Map with additional artifact properties
401 * @param timestamp Image tag
402 */
403def uploadPackageStep(art, file, properties, distribution, component, timestamp) {
404 return {
405 uploadDebian(
406 art,
407 file,
408 properties,
409 distribution,
410 component,
411 timestamp
412 )
413 }
414}
Oleksandr Hrabarab8ff4f2019-07-05 12:22:33 +0300415
416/**
417 * Get Helm repo for Artifactory
418 *
419 * @param art Artifactory connection object
420 * @param repoName Chart repository name
421 */
422def getArtifactoryProjectByName(art, repoName){
423 return restGet(art, "/repositories/${repoName}")
424}
425
426/**
427 * Get repo by packageType for Artifactory
428 *
429 * @param art Artifactory connection object
430 * @param packageType Repository package type
431 */
432def getArtifactoryProjectByPackageType(art, repoName){
433 return restGet(art, "/repositories?${packageType}")
434}
435
436/**
437 * Create Helm repo for Artifactory
438 *
439 * @param art Artifactory connection object
440 * @param repoName Chart repository name
441 * @param data Transmitted data
442 */
443def createArtifactoryChartRepo(art, repoName){
444 return restPut(art, "/repositories/${repoName}", '{"rclass": "local","handleSnapshots": false,"packageType": "helm"}')
445}
446
447/**
448 * Delete Helm repo for Artifactory
449 *
450 * @param art Artifactory connection object
451 * @param repoName Chart repository name
452 */
453def deleteArtifactoryChartRepo(art, repoName){
454 return restDelete(art, "/repositories/${repoName}")
455}
456
457/**
458 * Create Helm repo for Artifactory
459 *
460 * @param art Artifactory connection object
461 * @param repoName Repository Chart name
462 * @param chartName Chart name
463 */
464def publishArtifactoryHelmChart(art, repoName, chartName){
Oleksandr Hrabard3063462019-07-18 16:46:46 +0300465<<<<<<< HEAD
Oleksandr Hrabar04049342019-07-18 11:05:22 +0000466 return restPut(art, "/repositories/${repoName}", "${chartName}")
Oleksandr Hrabard3063462019-07-18 16:46:46 +0300467=======
468 return restPut2(art, "/${repoName}", "${chartName}")
469>>>>>>> 32c5611... Edited methods restCall, publishArtifactoryHelmChart, deleteArtifactoryHelmChart and added restPut2 for Artifactory.groovy
Oleksandr Hrabarab8ff4f2019-07-05 12:22:33 +0300470}
471
472/**
473 * Create Helm repo for Artifactory
474 *
475 * @param art Artifactory connection object
476 * @param repoName Repository Chart name
477 * @param chartName Chart name
478 */
479def deleteArtifactoryHelmChart(art, repoName, chartName){
Oleksandr Hrabar04049342019-07-18 11:05:22 +0000480 return restDelete(art, "/repositories/${repoName}", "${chartName}")
Oleksandr Hrabarab8ff4f2019-07-05 12:22:33 +0300481}