blob: b83a71499082871b31e45ffeb923d5ad9cf0d6a8 [file] [log] [blame]
Ruslan Kamaldinov90d4e672016-11-11 18:31:00 +03001package com.mirantis.mcp
2
sgarbuz60651572019-05-30 16:34:17 +03003import static org.yaml.snakeyaml.DumperOptions.FlowStyle.BLOCK
Pavlo Shchelokovskyybd0ca532018-12-13 18:50:00 +00004import java.util.zip.GZIPInputStream
5import java.util.zip.GZIPOutputStream
6
Sergey Reshetnyak70b1fe62017-01-31 22:27:06 +03007@Grab(group='org.yaml', module='snakeyaml', version='1.17')
8import org.yaml.snakeyaml.Yaml
sgarbuz60651572019-05-30 16:34:17 +03009import org.yaml.snakeyaml.DumperOptions
Sergey Reshetnyak70b1fe62017-01-31 22:27:06 +030010
Ruslan Kamaldinov90d4e672016-11-11 18:31:00 +030011/**
12 * https://issues.jenkins-ci.org/browse/JENKINS-26481
13 * fix groovy List.collect()
14 **/
15@NonCPS
Dmitry Burmistrovc28e9222018-07-16 18:46:18 +040016def constructString(ArrayList options, String keyOption, String separator = ' ') {
17 return options.collect { keyOption + it }.join(separator).replaceAll('\n', '')
Ruslan Kamaldinov90d4e672016-11-11 18:31:00 +030018}
19
20/**
21 * Generate current timestamp
22 *
23 * @param format Defaults to yyyyMMddHHmmss
24 */
25def getDatetime(format = "yyyyMMddHHmmss") {
Dmitry Burmistrovc28e9222018-07-16 18:46:18 +040026 def now = new Date()
27 return now.format(format, TimeZone.getTimeZone('UTC'))
Denis Egorenko8c606552016-12-07 14:22:50 +040028}
29
30/**
31 * Run tox with or without specified environment
32 * @param env String, name of environment
33 */
34def runTox(String env = null) {
35 if (env) {
36 sh "tox -v -e ${env}"
37 } else {
Dmitry Burmistrovc28e9222018-07-16 18:46:18 +040038 sh 'tox -v'
Denis Egorenko8c606552016-12-07 14:22:50 +040039 }
40}
Sergey Kulanove897d8f2017-01-23 16:44:03 +020041
42/**
Sergey Reshetnyak70b1fe62017-01-31 22:27:06 +030043 * Convert YAML document to Map object
44 * @param data YAML string
45 */
46@NonCPS
47def loadYAML(String data) {
48 def yaml = new Yaml()
49 return yaml.load(data)
50}
51
52/**
53 * Convert Map object to YAML string
54 * @param map Map object
55 */
56@NonCPS
57def dumpYAML(Map map) {
sgarbuz60651572019-05-30 16:34:17 +030058 DumperOptions options = new DumperOptions()
59 options.setPrettyFlow(true)
60 options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK)
61 def yaml = new Yaml(options)
Sergey Reshetnyak70b1fe62017-01-31 22:27:06 +030062 return yaml.dump(map)
63}
64
65/**
Sergey Kulanov21c936c2017-02-02 13:43:14 +020066 * Render jinja template
67 * @param templateVars String, A dict, a dict subclass, json or some keyword arguments
68 * @param templateFile String, jinja template file path
69 * @param resultFile String, result/generate file path
70 *
71 * Usage example:
72 *
73 * def common = new com.mirantis.mcp.Common()
74 * common.renderJinjaTemplate(
75 * "${NODE_JSON}",
76 * "${WORKSPACE}/inventory/inventory.cfg",
77 * "${WORKSPACE}/inventory/inventory.cfg")
78 * where NODE_JSON= data in json format
79 */
80def renderJinjaTemplate(String templateVars, String templateFile, String resultFile) {
81
82 sh """
83 python -c "
84import sys
85import jinja2
86from jinja2 import Template
87
88# Useful for very coarse version differentiation.
89PY2 = sys.version_info[0] == 2
90PY3 = sys.version_info[0] == 3
91PY34 = sys.version_info[0:2] >= (3, 4)
92
93if PY3:
94 string_types = str,
95else:
96 string_types = basestring
97
98
99def to_bool(a):
100 ''' return a bool for the arg '''
101 if a is None or type(a) == bool:
102 return a
103 if isinstance(a, string_types):
104 a = a.lower()
105 if a in ['yes', 'on', '1', 'true', 1]:
106 return True
107 else:
108 return False
109
110
111def generate(templateVars, templateFile, resultFile):
112 templateLoader = jinja2.FileSystemLoader(searchpath='/')
113 templateEnv = jinja2.Environment(loader=templateLoader)
114 templateEnv.filters['bool'] = to_bool
115 template = templateEnv.get_template(templateFile)
116 outputText = template.render(templateVars)
117 Template(outputText).stream().dump(resultFile)
118
119generate(${templateVars}, '${templateFile}', '${resultFile}')
120"
121 cat ${resultFile}
122 """
123}
124
125/**
Sergey Kulanove897d8f2017-01-23 16:44:03 +0200126 * Run function on k8s cluster
127 *
128 * @param config LinkedHashMap
129 * config includes next parameters:
130 * - label, pod label
131 * - function, code that should be run on k8s cluster
132 * - jnlpImg, jnlp slave image
133 * - slaveImg, slave image
134 *
135 * Usage example:
136 *
137 * def runFunc = new com.mirantis.mcp.Common()
138 * runFunc.runOnKubernetes ([
139 * function : this.&buildCalicoContainers,
140 * jnlpImg: 'docker-prod-virtual.docker.mirantis.net/mirantis/jenkins-slave-images/jnlp-slave:latest',
141 * slaveImg : 'sandbox-docker-dev-local.docker.mirantis.net/skulanov/jenkins-slave-images/calico-slave:1'
142 * ])
143 * // promotion example. In case of promotion we need only jnlp container
144 * def runFunc = new com.mirantis.mcp.Common()
145 * runFunc.runOnKubernetes ([
146 * jnlpImg: 'docker-prod-virtual.docker.mirantis.net/mirantis/jenkins-slave-images/jnlp-slave:latest',
147 * function : this.&promote_artifacts
148 * ])
149 */
150def runOnKubernetes(LinkedHashMap config) {
151
152
153 def jenkinsSlaveImg = config.get('slaveImg', 'none')
154 def jnlpSlaveImg = config.get('jnlpImg', 'none')
155 def lbl = config.get('label', "buildpod.${env.JOB_NAME}.${env.BUILD_NUMBER}".replace('-', '_').replace('/', '_'))
156 def toRun = config.get('function', 'none')
157
Sergey Kulanovb1aa0ff2017-01-23 17:48:44 +0200158 if (jnlpSlaveImg == 'none') {
Sergey Kulanove897d8f2017-01-23 16:44:03 +0200159 error('jnlp Slave image MUST be defined')
160 }
161
162 if (toRun == 'none'){
163 error('Code that should be run on k8s MUST be passed along with function parameter')
164 }
165
166 if (jenkinsSlaveImg == 'none'){
167 // we are running jnlp container only, since no jenkinsSlaveImg is specified, so
168 // we are in promotion mode
169 podTemplate(label: lbl,
170 containers: [
171 containerTemplate(
172 name: 'jnlp',
173 image: jnlpSlaveImg,
174 args: '${computer.jnlpmac} ${computer.name}'
175 )
176 ],
177 ) {
178 node(lbl){
179 container('jnlp') {
180 toRun()
181 }
182 }
183 }
184
185 } else {
186 podTemplate(label: lbl,
187 containers: [
188 containerTemplate(
189 name: 'jnlp',
190 image: jnlpSlaveImg,
191 args: '${computer.jnlpmac} ${computer.name}'
192 ),
193 containerTemplate(
194 name: 'k8s-slave',
195 image: jenkinsSlaveImg,
196 alwaysPullImage: false,
197 ttyEnabled: true,
198 privileged: true
199 )
200 ],
201 ) {
202 node(lbl){
203 container('k8s-slave') {
Sergey Kulanovb36e36e2017-01-23 18:26:40 +0200204 return toRun()
Sergey Kulanove897d8f2017-01-23 16:44:03 +0200205 }
206 }
207 }
208 } //else
209
210}
Pavlo Shchelokovskyybd0ca532018-12-13 18:50:00 +0000211
Pavlo Shchelokovskyyb1e6d0b2018-12-17 17:17:20 +0000212/**
213 * Compress a string with Gzip and encode result with Base64 encoding,
214 * useful for wire transfer of large text data over text-based protocols like HTTP
215 * @param s string to encode
216 * @return base64-encoded gzipped string
217*/
Pavlo Shchelokovskyybd0ca532018-12-13 18:50:00 +0000218def zipBase64(String s){
219 def targetStream = new ByteArrayOutputStream()
220 def zipStream = new GZIPOutputStream(targetStream)
221 zipStream.write(s.getBytes('UTF-8'))
222 zipStream.close()
223 def zippedBytes = targetStream.toByteArray()
224 targetStream.close()
Pavlo Shchelokovskyyb1e6d0b2018-12-17 17:17:20 +0000225 return zippedBytes.encodeBase64().toString()
Pavlo Shchelokovskyybd0ca532018-12-13 18:50:00 +0000226}
227
Pavlo Shchelokovskyyb1e6d0b2018-12-17 17:17:20 +0000228/**
229 * De-compress a base64-encoded gzipped string, reverts result of zipBase64
230 * @param compressed base64-endcoded gzipped string
231 * @return decoded decompressed string
232*/
Pavlo Shchelokovskyybd0ca532018-12-13 18:50:00 +0000233def unzipBase64(String compressed){
234 def inflaterStream = new GZIPInputStream(new ByteArrayInputStream(compressed.decodeBase64()))
235 def uncompressedStr = inflaterStream.getText('UTF-8')
236 return uncompressedStr
237}