-
Notifications
You must be signed in to change notification settings - Fork 13
/
Quay.groovy
217 lines (196 loc) · 8.19 KB
/
Quay.groovy
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
package com.puzzleitc.jenkins
import com.cloudbees.groovy.cps.NonCPS
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
/**
* Quay registry utility class.
*/
class Quay {
static String API_PATH = 'api/v1'
static String QUAY_URL = 'https://quay.io'
static String REPOSITORY_PATH = API_PATH + '/' + 'repository'
private final String credentialName
private final String registryUrl
private final Script script
/**
* This constructor sets the Jenkins 'script' class as the local script variable in order to execute steps (echo, etc).
* Connecting to the default registry (quay.io)
* @param script the script object (normally 'this')
* @param credentialName the name of the Credential holding the login token
*/
Quay(Script script, String credentialName) {
this(script, credentialName, '')
}
/**
* This constructor sets the Jenkins 'script' class as the local script variable in order to execute steps (echo, etc).
* @param script the script object (normally 'this')
* @param credentialName the name of the Credential holding the login token
* @param registryUrl optional param to set the url of the registry (defaults to quay.io)
*/
Quay(Script script, String credentialName, String registryUrl) {
mandatoryParameter('credentialName', credentialName)
this.script = script
this.credentialName = credentialName
if (registryUrl?.trim()) {
this.registryUrl = registryUrl
} else {
this.registryUrl = QUAY_URL
}
}
/**
* Returns the url of the registry that is used.
* @return the registry url
*/
String getRegistryUrl() {
return this.registryUrl
}
/**
* Reads the tag of the given repository and returns the manifest digest (sha).
* Returning null means tag not found.
*
* @param registryUserOrOrg user or organisation name that holds the repository
* @param repository name of the image repository
* @param tag name of the tag to get the sha of it's image
* @return the sha of the image with that tag
*/
String getTagManifest(String registryUserOrOrg, String repository, String tag) {
mandatoryParameter('registryUserOrOrg', registryUserOrOrg)
mandatoryParameter('repository', repository)
mandatoryParameter('tag', tag)
String url = registryUrl + '/' + REPOSITORY_PATH + '/' + registryUserOrOrg + '/' +
repository + '/' + 'tag' + '/' + '?onlyActiveTags=true' + '&specificTag=' + tag
this.script.withCredentials([this.script.string(credentialsId: this.credentialName, variable: 'bearerToken')]) {
return doGetTagManifestRequest(url, tag, this.script.bearerToken)
}
}
/**
* Adds the given tag to the image referenced by the sha.
*
* @param registryUserOrOrg user or organisation name that holds the repository
* @param repository name of the image repository
* @param existingTagSha the manifest digest (sha) of the image to add the new tag
* @param newTagName new tag for the image
*/
void addTag(String registryUserOrOrg, String repository, String existingTagSha, String newTagName) {
mandatoryParameter('registryUserOrOrg', registryUserOrOrg)
mandatoryParameter('repository', repository)
mandatoryParameter('existingTagSha', existingTagSha)
mandatoryParameter('newTagName', newTagName)
String url = registryUrl + '/' + REPOSITORY_PATH + '/' + registryUserOrOrg + '/' +
repository + '/' + 'tag' + '/' + newTagName
this.script.withCredentials([this.script.string(credentialsId: this.credentialName, variable: 'bearerToken')]) {
doAddTagRequest(url, existingTagSha, newTagName, this.script.bearerToken)
}
}
/**
* Executes the request to read the sha of the image.
* Extracted method to use @NonCPS
*
* @param url (api) to read the tag
* @param tag that should be read
* @param bearerToken login token
* @return the sha of the image with that tag
*/
@NonCPS
private String doGetTagManifestRequest(String url, String tag, String bearerToken) {
HttpURLConnection con = getRequest(url, bearerToken)
int httpResult = con.getResponseCode()
if (httpResult == HttpURLConnection.HTTP_OK) {
String contentText = con.content.text
con.disconnect()
Object responseContent = new JsonSlurper().parseText(contentText)
List tagsList = (List) responseContent.get("tags")
switch (tagsList.size()) {
case 0:
this.script.echo('Tag not found')
return null
case 1:
String sha = tagsList[0].manifest_digest
this.script.echo('SHA found: ' + sha)
return sha
default:
this.script.error('Tag "' + tag + '" not unique. Tag read returned more than one entry.')
}
} else {
this.script.error("""
Tag '${tag}' read error.
HTTP response code: ${httpResult}
HTTP response message:
${con.getResponseMessage()}""")
}
}
/**
* Executes the request to add the tag.
* Extracted method to use @NonCPS
*
* @param url (api) to add the tag
* @param existingTagSha the manifest digest (sha) of the image to add the new tag
* @param newTagName new tag for the image
* @param bearerToken login token
*/
@NonCPS
private void doAddTagRequest(String url, String existingTagSha, String newTagName, String bearerToken) {
HttpURLConnection con = putRequest(url, [manifest_digest: existingTagSha], bearerToken)
int httpResult = con.getResponseCode()
if (httpResult == HttpURLConnection.HTTP_OK || httpResult == HttpURLConnection.HTTP_CREATED) {
String contentText = con.content.text
con.disconnect()
Object responseContent = new JsonSlurper().parseText(contentText)
this.script.echo('Add tag result: ' + responseContent.toString())
} else {
this.script.error("""
Tag '${newTagName}' pointing to SHA '${existingTagSha}' creation error.
HTTP response code: ${httpResult}
HTTP response message:
${con.getResponseMessage()}""")
}
}
/**
* Prepare a GET request
*
* @param url to open the connection to
* @param bearerToken login token
* @return the http connection
*/
@NonCPS
private static HttpURLConnection getRequest(String url, String bearerToken) {
URL targetURL = new URL(url)
HttpURLConnection con = (HttpURLConnection) targetURL.openConnection()
con.setDoOutput(true)
con.setDoInput(true)
con.setRequestProperty("Content-Type", "application/json; charset=UTF-8")
con.setRequestProperty("Accept", "application/json")
con.setRequestProperty('Authorization', 'Bearer ' + bearerToken)
return con
}
/**
* Prepare a PUT request
*
* @param url to open the connection to
* @param bodyContent map holding the body of the request. Will be transformed to JSON.
* @param bearerToken login token
* @return the http connection
*/
@NonCPS
private static HttpURLConnection putRequest(String url, Map bodyContent, String bearerToken) {
URL targetURL = new URL(url)
String body = JsonOutput.toJson(bodyContent)
HttpURLConnection con = (HttpURLConnection) targetURL.openConnection()
con.setDoOutput(true)
con.setDoInput(true)
con.setRequestMethod("PUT")
con.setRequestProperty("Content-Type", "application/json; charset=UTF-8")
con.setRequestProperty("Accept", "application/json")
con.setRequestProperty('Authorization', 'Bearer ' + bearerToken)
OutputStreamWriter wr = new OutputStreamWriter(con.getOutputStream())
wr.write(body.toString())
wr.flush()
return con
}
@NonCPS
private static void mandatoryParameter(String parameterName, parameter) {
if (!(parameter?.trim())) {
throw new IllegalArgumentException("Missing parameter '${parameterName}'")
}
}
}