blob: 12470ad6093aadacf379946c2ca3e3bfe6c6180a [file] [log] [blame]
Ryabin Sergeya78ee8d2017-02-07 12:52:18 +04001package com.mirantis.mcp
2
3/*
4 Example usage:
5
6 def ccpCiCd = new com.mirantis.mcp.CCPCICD().newInstance(this, env)
7
8 ccpCiCd.applyPipelineParameters()
9
10 ccpCiCd.fetchEnvConfiguration()
11 ccpCiCd.parametrizeConfig()
12
13 ccpCiCd.build()
14 ccpCiCd.deploy()
15 ccpCiCd.cleanup()
16*/
17
18/*
19 Since groovy-cps demands that local variables may be serialized,
20 any locally defined classes must also be serializable
21
22 More details: https://issues.jenkins-ci.org/browse/JENKINS-32213
23*/
24
25public class ccpCICD implements Serializable {
26
27 /*
28 Endless loop in DefaultInvoker.getProperty when accessing field
29 via getter/setter without @
30
31 This issue fixed in groovy pipeline plugin 2.25
32
33 More details https://issues.jenkins-ci.org/browse/JENKINS-31484
34 */
35
36 /*
37 nodeContext - is a context of which node the job is executed
38 through this context, CCPCICD class able to use
39 dir() writeFile() sh() and other workflow basic steps
40
41 See more https://jenkins.io/doc/pipeline/steps/workflow-basic-steps/
42
43 nodeContext must be passed in class constructor
44 */
45 def nodeContext = null
46
47 /*
48 Job parameters are accessed through environment
49 */
50 def env = null
51
52 /*
53 Directory where stored environment configuration
54 */
55 def envConfigurationPath = null
56 def File envConfiguration = null
57
58 /*
59 Path to entry point yaml in environment directory
60 */
61 def ccpConfigPath = null
62
63 def File virtualEnv = null // Currently not supported
64
65 def URI configurationRepo = null
66
67 /*
68 parsed `ccp config dump` output
69 */
70
71 def Map ccpConfig = null
72
73 /*
74 Part of ccp parameters which will be overwritten by job
75 parametrizeConfig() method
76
77 */
78 def Map ciConfig = [
79 'registry' : [
80 'address' : null,
81 'username' : null,
82 'password' : null,
83 ],
84 'kubernetes' : [
85 'namespace' : 'ccp',
86 'server' : null,
87 'insecure' : true,
88 'username' : null,
89 'password' : null
90 ],
91 'repositories': [
92 'path' : null
93 ]
94
95 ]
96
97 // Using loadYAML and dumpYAML from common library
98 def common = new com.mirantis.mcp.Common()
99
100 // default CCP repo (using in upgrade)
101 def URI ccpSource = new URI('git+https://gerrit.mcp.mirantis.net/ccp/fuel-ccp')
102
103 def public void setConfURL(URI confURI) {
104 if (confURI == null) {
105 throw new IllegalArgumentException('Invalid argument')
106 }
107 this.@configurationRepo = confURI
108 }
109
110 def public URI getConfURL() {
111 return this.@configurationRepo
112 }
113
114 /*
115 According JENKINS-31314 constructor can be used only for assignments properties
116 More details: https://issues.jenkins-ci.org/browse/JENKINS-31314
117 */
118 def public ccpCICD(nodeContext, env) {
119 this.@nodeContext = nodeContext
120 this.@env = env
121 }
122
123 /*
124 Transform jenkins job arguments to ccp configuration
125 Parameters can be overriden with customParameters Map
126
127 Example usage:
128 applyPipelineParameters([
129
130 'images': [
131 'image_specs': [
132 'etcd': [
133 'tag': '41a45e5a9db5'
134 ]
135 ]
136 ]
137
138 ])
139
140 */
141
142 def public void applyPipelineParameters(Map customParameters = null) {
143
144 if (this.@env.CONF_GERRIT_URL != null) {
145 this.setConfURL(new URI(this.@env.CONF_GERRIT_URL))
146 }
147
148
149 if (this.@env.CONF_ENTRYPOINT != null) {
150 this.setCcpConfigPath(new File(this.@env.CONF_ENTRYPOINT))
151 }
152
153 if (this.@env.KUBERNETES_URL != null) {
154 this.setKubernetesURL(new URI(this.@env.KUBERNETES_URL), this.@env.CREDENTIALS_ID)
155 } else {
156 this.@ciConfig.remove('kubernetes')
157 }
158
159 if (this.@env.DOCKER_REGISTRY != null) {
160 this.setRegistry(new URI(this.@env.DOCKER_REGISTRY), this.env.DOCKER_REGISTRY_CREDENTIAL_ID ? this.env.DOCKER_REGISTRY_CREDENTIAL_ID : 'artifactory')
161 } else {
162 this.@ciConfig.remove('registry')
163 }
164
165
166 this.@ciConfig['repositories']['path'] = env.WORKSPACE + '/repos'
167
168 if (customParameters != null) {
169 this.@ciConfig = this.@ciConfig + customParameters
170 }
171 }
172
173 def public File getCcpConfigPath() {
174 return this.@ccpConfigPath
175 }
176
177 def public void setCcpConfigPath(File configPath) {
178 this.@ccpConfigPath = configPath
179 }
180
181 /*
182 Set k8s endpoint and credentials
183
184 Example usage:
185 this.setKubernetesURL(new URI('https://host:443'), 'kubernetes-api')
186 */
187
188 def public void setKubernetesURL(URI kubernetesURL, String credentialId) {
189 if (credentialId != null) {
190 this.@nodeContext.withCredentials([
191 [
192 $class : 'UsernamePasswordMultiBinding',
193 credentialsId : credentialId,
194 passwordVariable : 'K8S_PASSWORD',
195 usernameVariable : 'K8S_USERNAME'
196 ]
197 ]) {
198 this.@ciConfig['kubernetes']['username'] = env.K8S_USERNAME
199 this.@ciConfig['kubernetes']['password'] = env.K8S_PASSWORD
200 }
201 }
202
203 /* override parameters from URI (if present) */
204 if (kubernetesURL.getUserInfo()) {
205 //TODO(sryabin) properly parse return from getUserInfo()
206 this.@ciConfig['kubernetes']['username'] = kubernetesURL.getUserInfo()
207 }
208
209 this.@ciConfig['kubernetes']['server'] = kubernetesURL.toString()
210 }
211
212 def public void setRegistry(String registryEndpoint, String credentialId) {
213 if (credentialId) {
214 this.@nodeContext.withCredentials([
215 [
216 $class : 'UsernamePasswordMultiBinding',
217 credentialsId : credentialId,
218 passwordVariable : 'REGISTRY_PASSWORD',
219 usernameVariable : 'REGISTRY_USERNAME'
220 ]
221 ]) {
222 this.@ciConfig['registry']['username'] = env.REGISTRY_USERNAME
223 this.@ciConfig['registry']['password'] = env.REGISTRY_PASSWORD
224 }
225 } else {
226 this.@ciConfig['registry'].remove('username')
227 this.@ciConfig['registry'].remove('password')
228 }
229
230 this.@ciConfig['registry']['address'] = registryEndpoint;
231 }
232
233 def public setEnvConfigurationDir() {
234 // TODO(sryabin) cleanup this dir in desctructor
235 this.@envConfigurationPath = File.createTempDir(this.@env.WORKSPACE + '/envConfiguration', 'ccpci')
236 }
237
238 def public File getEnvConfigurationDir() {
239 return this.@envConfigurationPath
240 }
241
242
243
244 def public File fetchEnvConfiguration() {
245
246 def gitTools = new com.mirantis.mcp.Git()
247
248 this.setEnvConfigurationDir()
249
250 gitTools.gitSSHCheckout ([
251 credentialsId : this.@env.CONF_GERRIT_CREDENTIAL_ID,
252 branch : "master",
253 host : this.getConfURL().getHost(),
254 port : this.getConfURL().getPort(),
255 project: this.getConfURL().getPath(),
256 targetDir: this.getEnvConfigurationDir().toString()
257 ])
258
259 return this.getEnvConfigurationDir()
260 }
261
262 def public String configDump() {
263 return this.ccpExecute('config dump')
264 }
265
266 /*
267 merge ccp configuration from repo and custom parameters from job arguments
268 */
269 def public Map parametrizeConfig() {
270 this.fetchEnvConfiguration()
271 this.@ccpConfig = this.@common.loadYAML(this.configDump())
272 this.setCcpConfigPath(new File('parametrized.yaml'));
273 this.@nodeContext.writeFile file: this.getEnvConfigurationDir().toString() + '/' + this.getCcpConfigPath().toString(), \
274 text: this.@common.dumpYAML(this.@ccpConfig + this.@ciConfig)
275 }
276
277 def public Map getConfig() {
278 return this.@ccpConfig
279 }
280
281
282 /*
283 Example usage:
284 ci.ccpExecute('config dump')
285 */
286 def public String ccpExecute(args) {
287 // TODO(sryabin) pass custom args from class property, like debug
288 def String output = null;
289 this.@nodeContext.dir(this.getEnvConfigurationDir().toString()) {
290 output = this.@nodeContext.sh(
291 script: "PYTHONWARNINGS='ignore:Unverified HTTPS request' ccp --config-file " + this.getCcpConfigPath().toString() + " ${args}",
292 returnStdout: true
293 )
294 }
295 return output
296 }
297
298 /*
299 Update fuel-ccp
300
301 @param virtualEnv File, path to python virtual environment, currently not supported
302 @param source URI, if not present, use upstream
303 @param upgradePip Boolean, upgrade pip if required
304
305 Usage example:
306 upgradeCcp(new File('/home/ubuntu/.venv'), new URI('http://github.com/openstack/fuel-ccp'))
307 */
308 def public void upgradeCcp(File virtualEnv = null, URI source, Boolean upgradePip = false) {
309 if (virtualEnv) {
310 throw new UnsupportedOperationException('Python virtual environments not implemented')
311 }
312
313 try {
314 def output = this.@nodeContext.sh(
315 script: 'pip install --user --upgrade ' + ((source != null) ? source : this.@ccpSource).toString(),
316 returnStdout: true
317 )
318 } catch (e) {
319 // TODO(sryabin) catch in stderr "You should consider upgrading via the 'pip install --upgrade pip' command."
320 if (upgradePip == true) {
321 this.@nodeContext.sh(
322 script: 'pip install --upgrade pip',
323 returnStdout: true
324 )
325 }
326 // FIXME(sryabin) infinity loop
327 //this.upgradeCcp(virtualEnv, source, false)
328 }
329 }
330 def public void upgradeCcp() {
331 this.upgradeCcp(null, null, true)
332 }
333
334 def build() {
335 //TODO(sryabin) implement build -c functionality
336 return this.ccpExecute('build')
337 }
338
339 def deploy() {
340 //TODO(sryabin) implement build -c functionality
341 return this.ccpExecute('deploy')
342 }
343
344 def cleanup() {
345 try {
346 this.ccpExecute('cleanup')
347 } catch (err) {
348 this.ccpExecute('cleanup --skip-os-cleanup')
349 }
350 }
351
352 def fetch() {
353 // TODO(sryabin) implement fetch -f functioanlity
354 return this.ccpExecute('fetch')
355 }
356}
357
358/*
359 Workaround for scope limitation, and
360 org.jenkinsci.plugins.scriptsecurity.sandbox.RejectedAccessException: unclassified new
361
362 def ccpCiCd = new com.mirantis.mcp.CCPCICD()
363 def CCPCICD ci = new ccpCiCd.ccpCICD(this, env) return no such CCPCICD()
364
365 Example usage:
366 def ccpCiCd = new com.mirantis.mcp.CCPCICD().newInstance(this, env)
367
368*/
369
370def newInstance(nodeContext, env) {
371 return new ccpCICD(nodeContext,env)
372}