1. commit 38639fed9957f40c71ab1cd0ab767c33c79ca6b6
  2. Author: Loic Dachary <[email protected]>
  3. Date: Thu Nov 5 07:54:35 2015 +0100
  4. jenkins: no longer use jenkins
  5. Signed-off-by: Loic Dachary <[email protected]>
  6. diff --git a/ChangeLog b/ChangeLog
  7. index 3efedea..712739e 100644
  8. --- a/ChangeLog
  9. +++ b/ChangeLog
  10. @@ -1,4 +1,4 @@
  11. -commit c9ed7e52758997a38724d549d764fa6d9b3b3193
  12. +commit 36232fe550a9a07bbfcfdcdec6fd651186ba2635
  13. Author: Loic Dachary <[email protected]>
  14. Date: Tue Apr 28 12:04:04 2015 +0200
  15. diff --git a/ceph_workbench/jenkins.py b/ceph_workbench/jenkins.py
  16. deleted file mode 100644
  17. index e01df85..0000000
  18. --- a/ceph_workbench/jenkins.py
  19. +++ /dev/null
  20. @@ -1,575 +0,0 @@
  21. -# -*- mode: python; coding: utf-8 -*-
  22. -#
  23. -# Copyright (C) 2015 <[email protected]>
  24. -#
  25. -# Author: Loic Dachary <[email protected]>
  26. -#
  27. -# This program is free software: you can redistribute it and/or modify
  28. -# it under the terms of the GNU Affero General Public License as
  29. -# published by the Free Software Foundation, either version 3 of the
  30. -# License, or (at your option) any later version.
  31. -#
  32. -# This program is distributed in the hope that it will be useful,
  33. -# but WITHOUT ANY WARRANTY; without even the implied warranty of
  34. -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  35. -# GNU Affero General Public License for more details.
  36. -#
  37. -# You should have received a copy of the GNU Affero General Public License
  38. -# along with this program. If not, see `<http://www.gnu.org/licenses/>`.
  39. -#
  40. -import argparse
  41. -import grp
  42. -import json
  43. -import logging
  44. -import os
  45. -import re
  46. -import requests
  47. -import shutil
  48. -import subprocess
  49. -import time
  50. -
  51. -from ceph_workbench import util
  52. -
  53. -
  54. -class NoCredentials(Exception):
  55. - pass
  56. -
  57. -cred_template = """
  58. -{
  59. - "domainCredentials": {
  60. - "domain": {
  61. - "name": "",
  62. - "description": ""
  63. - },
  64. - "credentials": [
  65. - {
  66. - "scope": "GLOBAL",
  67. - "id": "",
  68. - "username": "{USERNAME}",
  69. - "description": "{DESCRIPTION}",
  70. - "privateKeySource": {
  71. - "value": "1",
  72. - "privateKeyFile": "{KEY_FILE}",
  73. - "stapler-class": "com.cloudbees.jenkins.plugins\
  74. -.sshcredentials.impl.BasicSSHUserPrivateKey$FileOnMasterPrivateKeySource"
  75. - },
  76. - "passphrase": "",
  77. - "stapler-class": "com.cloudbees.jenkins.plugins\
  78. -.sshcredentials.impl.BasicSSHUserPrivateKey",
  79. - "kind": "com.cloudbees.jenkins.plugins\
  80. -.sshcredentials.impl.BasicSSHUserPrivateKey"
  81. - }
  82. - ]
  83. - }
  84. -}
  85. -"""
  86. -
  87. -
  88. -class Jenkins:
  89. -
  90. - def __init__(self, args):
  91. - self.args = args
  92. - self.args.port_range = 3000
  93. - self.args.jenkins_slave = 'ceph-jenkins-slave'
  94. - self.args.jenkins_master = 'ceph-jenkins-master'
  95. - self.args.libguestfs = 'libguestfs'
  96. - self.args.jar_dir = '/tmp'
  97. -
  98. - def run(self):
  99. - if self.args.publish_build_to_pull_request:
  100. - return self.publish_build_to_pull_request()
  101. - elif self.args.jenkins_make_check_bot:
  102. - return subprocess.call([self.args.libdir +
  103. - '/docker-make-check-bot'])
  104. - elif self.args.jenkins_docker_run_slave:
  105. - return self.run_slave('docker', self.args.jenkins_docker_run_slave)
  106. - elif self.args.jenkins_qemu_prepare:
  107. - return self.prepare_qemu_img(self.args.jenkins_qemu_image)
  108. - elif self.args.jenkins_qemu_run_slave:
  109. - return self.run_slave('qemu', self.args.jenkins_qemu_run_slave)
  110. - elif self.args.jenkins_docker_destroy_slave:
  111. - return self.docker_destroy_slave(
  112. - self.args.jenkins_docker_destroy_slave)
  113. - elif self.args.jenkins_run_master:
  114. - return self.run_master()
  115. -
  116. - @staticmethod
  117. - def get_parser():
  118. - parser = argparse.ArgumentParser(
  119. - description="Jenkins",
  120. - conflict_handler='resolve',
  121. - )
  122. - parser.add_argument('--jenkins-run-master',
  123. - action='store_true',
  124. - help='run the jenkins master')
  125. - parser.add_argument('--jenkins-docker-run-slave',
  126. - help=('run the jenkins slave in docker and '
  127. - 'register it to the jenkins master'))
  128. - parser.add_argument('--jenkins-docker-destroy-slave',
  129. - help='destroy the jenkins docker slave')
  130. - parser.add_argument('--jenkins-qemu-run-slave',
  131. - help=('run the jenkins slave in qemu and '
  132. - 'register it to the jenkins master'))
  133. - parser.add_argument('--jenkins-qemu-prepare',
  134. - action='store_true',
  135. - help=('create a directory with '
  136. - 'a ready to use image'))
  137. - parser.add_argument('--jenkins-qemu-image',
  138. - help='for instance ubuntu-14.04-i386')
  139. - parser.add_argument('--jenkins-qemu-ram',
  140. - default='512',
  141. - help='RAM for qemu, in GB')
  142. - parser.add_argument('--jenkins-qemu-ssh-port',
  143. - help='ssh port to connect to the qemu instance')
  144. - parser.add_argument('--jenkins-make-check-bot',
  145. - help='run the jenkins-make-check-bot helper')
  146. - parser.add_argument('--publish-build-to-pull-request',
  147. - help='add a comment to pull request XXX')
  148. - parser.add_argument('--jenkins-git-branch',
  149. - help=('only for --publish-build-to-pull-request, '
  150. - 'force the GIT_BRANCH value'))
  151. - parser.add_argument('--jenkins-host',
  152. - help='hostname of the jenkins master')
  153. - parser.add_argument('--jenkins-port',
  154. - default='80',
  155. - help='port of the jenkins master')
  156. - parser.add_argument('--jenkins-ssh-host',
  157. - help='jenkins host name for ssh protocol')
  158. - parser.add_argument('--jenkins-ssh-port',
  159. - default='22',
  160. - help='jenkins port for ssh protocol')
  161. - parser.add_argument('--jenkins-ssh-key',
  162. - help='jenkins ssh private key')
  163. - parser.add_argument('--jenkins-user',
  164. - help='jenkins user name for API authentication')
  165. - parser.add_argument('--jenkins-password',
  166. - help='jenkins password for API authentication')
  167. - parser.add_argument('--github-repo',
  168. - help='GitHub repo (for instance ceph/ceph)')
  169. - parser.add_argument('--github-token',
  170. - help='GitHub authentication token')
  171. - parser.add_argument('--libdir',
  172. - help='directory containing helpers programs')
  173. - parser.add_argument('--datadir',
  174. - help='directory for persistent data')
  175. - parser.add_argument('--jenkins-home',
  176. - help='jenkins home directory, on the docker host',
  177. - default=os.environ['HOME'])
  178. - return parser
  179. -
  180. - @staticmethod
  181. - def factory(argv):
  182. - return Jenkins(Jenkins.get_parser().parse_args(argv))
  183. -
  184. - def get_url(self):
  185. - return ('http://' + self.args.jenkins_host +
  186. - ':' + self.args.jenkins_port)
  187. -
  188. - def publish_build_to_pull_request(self):
  189. - r = requests.get(self.get_url() + '/' +
  190. - self.args.publish_build_to_pull_request + '/api/json')
  191. - r.raise_for_status()
  192. - report = r.json()
  193. - logging.debug("publish_build_to_pull_request: downstream " +
  194. - str(report))
  195. - for action in report['actions']:
  196. - if "causes" in action:
  197. - cause = action['causes'][0]
  198. - upstream = (cause['upstreamUrl'] + '/' +
  199. - str(cause['upstreamBuild']))
  200. - r = requests.get(self.get_url() + '/' + upstream + '/api/json')
  201. - r.raise_for_status()
  202. - report = r.json()
  203. - logging.debug("publish_build_to_pull_request: upstream " + str(report))
  204. - if self.args.jenkins_git_branch:
  205. - branch_name = self.args.jenkins_git_branch
  206. - else:
  207. - for action in report['actions']:
  208. - if "lastBuiltRevision" in action:
  209. - branch = action["lastBuiltRevision"]["branch"][0]
  210. - branch_name = branch['name']
  211. - runs = {}
  212. - for run in report['runs']:
  213. - if run['number'] == report['number']:
  214. - r = requests.get(run['url'] + 'api/json')
  215. - r.raise_for_status()
  216. - runs[run['url']] = r.json()['result']
  217. - body = report['result'] + ": " + report['url'] + "\n"
  218. - for url in sorted(runs):
  219. - body += "* " + runs[url] + " " + url + "/console\n"
  220. - logging.debug("publish_build_to_pull_request: " + body)
  221. - if 'pull/' in branch_name:
  222. - (pr,) = re.findall('\d+$', branch_name)
  223. - payload = {'body': body}
  224. - r = requests.post('https://api.github.com/repos/' +
  225. - self.args.github_repo + '/issues/' +
  226. - pr + '/comments?access_token=' +
  227. - self.args.github_token,
  228. - data=json.dumps(payload))
  229. - r.raise_for_status()
  230. - return r.json()['body']
  231. - else:
  232. - return body
  233. -
  234. - def get_cli_jar(self):
  235. - return self.args.jar_dir + '/jenkins-cli.jar'
  236. -
  237. - def run_cli(self, *args, **kwargs):
  238. - jar = self.get_cli_jar()
  239. - if not os.path.exists(jar):
  240. - r = requests.get(self.get_url() +
  241. - '/jnlpJars/jenkins-cli.jar')
  242. - with open(jar, 'wb') as fd:
  243. - for chunk in r.iter_content(4096):
  244. - fd.write(chunk)
  245. - cmd = ['java', '-jar', jar, '-s', self.get_url()] + list(args)
  246. - s = subprocess.Popen(cmd,
  247. - stdin=subprocess.PIPE,
  248. - stdout=subprocess.PIPE,
  249. - stderr=subprocess.PIPE)
  250. - if 'payload' in kwargs:
  251. - payload = kwargs['payload'].encode('utf-8')
  252. - else:
  253. - payload = None
  254. - (stdoutdata, stderrdata) = s.communicate(input=payload)
  255. - stdoutdata = stdoutdata.decode('utf-8')
  256. - stderrdata = stderrdata.decode('utf-8')
  257. - logging.debug(" ".join(cmd) + ":" +
  258. - " stdout " + stdoutdata +
  259. - " stderr " + stderrdata)
  260. - return stdoutdata + stderrdata
  261. -
  262. - def master_port(self, name):
  263. - (number,) = re.findall('(\d+)$', name)
  264. - return str(self.args.port_range + int(number))
  265. -
  266. - def slave_port(self, name):
  267. - (number,) = re.findall('(\d+)$', name)
  268. - return str(4000 + int(number))
  269. -
  270. - def find_node(self, name):
  271. - r = requests.get(self.get_url() + '/computer/api/json')
  272. - r.raise_for_status()
  273. - return name in map(lambda x: x['displayName'], r.json()['computer'])
  274. -
  275. - def destroy_node(self, name):
  276. - if self.find_node(name):
  277. - self.run_cli("delete-node", name)
  278. -
  279. - def next_node_name(self):
  280. - r = requests.get(self.get_url() + '/computer/api/json')
  281. - r.raise_for_status()
  282. - nodes = r.json()['computer']
  283. - highest = 0
  284. - for node in nodes:
  285. - name_with_number = re.findall('(\d+)$', node['displayName'])
  286. - if name_with_number:
  287. - highest = max(highest, int(name_with_number[0]))
  288. - highest += 1
  289. - return "slave%03d" % highest
  290. -
  291. - def create_node(self, name):
  292. - # assume the master is running in docker and slaves are accessed
  293. - # via a ssh tunel open on the docker host
  294. - credentials = self.slave_credential_id()
  295. - port = self.master_port(name)
  296. - payload = """
  297. -<?xml version="1.0" encoding="UTF-8"?>
  298. -<slave>
  299. - <name>{name}</name>
  300. - <description></description>
  301. - <remoteFS>/home/jenkins</remoteFS>
  302. - <numExecutors>1</numExecutors>
  303. - <mode>NORMAL</mode>
  304. - <retentionStrategy class="hudson.slaves.RetentionStrategy$Always"/>
  305. - <launcher class="hudson.plugins.sshslaves.SSHLauncher"
  306. - plugin="[email protected]">
  307. - <host>localhost</host>
  308. - <port>{port}</port>
  309. - <credentialsId>{credentials}</credentialsId>
  310. - <maxNumRetries>0</maxNumRetries>
  311. - <retryWaitTime>0</retryWaitTime>
  312. - </launcher>
  313. - <label>docker ceph-workbench</label>
  314. - <nodeProperties/>
  315. - <userId>admin</userId>
  316. -</slave>
  317. -""".format(port=port,
  318. - name=name,
  319. - credentials=credentials)
  320. - logging.debug('create_node: ' + name + ' ' + payload)
  321. - self.run_cli('create-node', name, payload=payload)
  322. - r = requests.get(self.get_url() +
  323. - '/computer/' + name + '/api/json')
  324. - r.raise_for_status()
  325. - return r.json()
  326. -
  327. - def run_slave(self, kind, name):
  328. - if name == 'new':
  329. - name = self.next_node_name()
  330. - self.create_node(name)
  331. - if kind == 'docker':
  332. - return self.docker_run_slave(name)
  333. - elif kind == 'qemu':
  334. - return self.qemu_run_slave(name)
  335. - else:
  336. - raise Exception(kind + ' is not docker nor qemu')
  337. -
  338. - def slave_credential_id(self):
  339. - auth = (self.args.jenkins_user, self.args.jenkins_password)
  340. - r = requests.get(self.get_url() +
  341. - '/credential-store/domain/_/api/json',
  342. - auth=auth)
  343. - r.raise_for_status()
  344. - slave_description = 'for jenkins slaves'
  345. - credentials = {}
  346. - for credential in list(r.json()['credentials']):
  347. - c = requests.get(self.get_url() +
  348. - ('/credential-store/domain/_/credential/' +
  349. - credential + '/api/json'),
  350. - auth=auth)
  351. - c.raise_for_status()
  352. - credentials[c.json()['description']] = credential
  353. -
  354. - if slave_description in credentials:
  355. - return credentials[slave_description]
  356. - else:
  357. - raise NoCredentials('no "' + slave_description +
  358. - '" credential in ' +
  359. - str(credentials))
  360. -
  361. - def fetch_ssh_key(self, directory):
  362. - if not os.path.exists(directory):
  363. - os.makedirs(directory, 0o755)
  364. - dotssh = directory + '/.ssh'
  365. - if not os.path.exists(dotssh):
  366. - os.mkdir(dotssh, 0o700)
  367. - if self.args.jenkins_ssh_key:
  368. - key = '-i ' + self.args.jenkins_ssh_key + ' '
  369. - else:
  370. - key = ''
  371. - if self.args.jenkins_ssh_port:
  372. - port = '-P ' + self.args.jenkins_ssh_port + ' '
  373. - else:
  374. - port = ''
  375. - known_hosts = self.ignore_known_hosts()
  376. - util.sh('scp ' + known_hosts + key + port +
  377. - self.args.jenkins_ssh_host +
  378. - ':/var/jenkins_home/.ssh/id_rsa* ' +
  379. - dotssh)
  380. - shutil.copyfile(dotssh + '/id_rsa.pub',
  381. - dotssh + '/authorized_keys')
  382. - os.chmod(dotssh + '/authorized_keys', 0o600)
  383. -
  384. - @staticmethod
  385. - def ignore_known_hosts():
  386. - return ('-o UserKnownHostsFile=/dev/null '
  387. - '-o StrictHostKeyChecking=no ')
  388. -
  389. - def tunnel(self, port):
  390. - script = ("ssh {known_hosts} " +
  391. - " -p {jenkins_port} -f -N -R {port}:localhost:22 " +
  392. - " {jenkins_host}")
  393. - return script.format(known_hosts=self.ignore_known_hosts(),
  394. - port=port,
  395. - jenkins_port=self.args.jenkins_ssh_port,
  396. - jenkins_host=self.args.jenkins_ssh_host)
  397. -
  398. - def qemu_get_img(self):
  399. - image = self.args.jenkins_qemu_image
  400. - image_dir = self.args.datadir + "/" + image
  401. - if (os.path.exists(image_dir) and
  402. - os.path.exists(image_dir + '/.ssh')):
  403. - return image_dir
  404. - elif self.docker_p():
  405. - return self.prepare_qemu_img(image)
  406. - else:
  407. - host = self.args.jenkins_ssh_host
  408. - util.sh("rsync -avz " + host + ":" + image + "/ " +
  409. - image_dir + "/")
  410. - return image_dir
  411. -
  412. - def qemu_ssh(self, name, command):
  413. - slave_port = self.slave_port(name)
  414. - known_hosts = self.ignore_known_hosts()
  415. - image = self.args.jenkins_qemu_image
  416. - key = self.args.datadir + '/' + image + '/.ssh/id_rsa'
  417. - return util.sh("ssh -p " + slave_port + " " + known_hosts + " " +
  418. - " -i " + key + " jenkins@localhost " + command)
  419. -
  420. - def qemu_destroy_slave(self, name):
  421. - self.qemu_ssh(name, 'sudo halt -p')
  422. - return self.destroy_node(name)
  423. -
  424. - def qemu_run_slave(self, name):
  425. - image = self.args.jenkins_qemu_image
  426. - image_dir = self.qemu_get_img()
  427. - ram = self.args.jenkins_qemu_ram
  428. - if not os.path.exists(image_dir + '/' + image + '.img'):
  429. - shutil.copyfile(image_dir + '/' + image + '.orig',
  430. - image_dir + '/' + image + '.img')
  431. - master_port = self.master_port(name)
  432. - slave_port = self.slave_port(name)
  433. - util.sh(('cd {image_dir} ; ' +
  434. - 'kvm -name {name} ' +
  435. - '-m {ram} -smp {cpus},sockets=1 ' +
  436. - '-drive file={image}.img,if=virtio ' +
  437. - '-net nic -net user,hostfwd=tcp::{slave_port}-:22 ' +
  438. - '-kernel vmlinuz-* -initrd initrd.* ' +
  439. - '-append "root=/dev/vda1 ro ds=nocloud-net ' +
  440. - ' brd.rd_size={rd_size} ' +
  441. - ' master_port={master_port}" ' +
  442. - '-display none').format(ram=ram,
  443. - slave_port=slave_port,
  444. - master_port=master_port,
  445. - name=name,
  446. - cpus=str(8),
  447. - rd_size=str(16 * 1024 * 1024),
  448. - image=image,
  449. - image_dir=image_dir))
  450. -
  451. - def prepare_qemu_img(self, image):
  452. - self.docker_build_libguestfs()
  453. - libdir = os.path.abspath(self.args.libdir)
  454. - datadir = os.path.abspath(self.args.datadir)
  455. - image_dir = datadir + '/' + image
  456. - self.fetch_ssh_key(image_dir)
  457. - script = """\
  458. -#!/bin/sh -e
  459. -mkfs -t ext4 /dev/ram1
  460. -mkdir -p /home/jenkins/workspace
  461. -mount /dev/ram1 /home/jenkins/workspace
  462. -chown jenkins /home/jenkins/workspace
  463. -port=$(perl -ne 'print if(s/.*master_port=([0-9]*).*/$1/)' < /proc/cmdline)
  464. -if test "$port" ; then
  465. - su - -c "{tunnel}" jenkins &
  466. -fi
  467. - """.format(tunnel=self.tunnel('$port'))
  468. - open(image_dir + '/rc.local', 'w').write(script)
  469. - util.sh('docker run --rm ' +
  470. - ' -v ' + datadir + ':' + datadir +
  471. - ' -v ' + libdir + ':' + libdir +
  472. - ' -w ' + datadir +
  473. - ' ' + self.args.libguestfs + ' ' + libdir + '/' +
  474. - 'create-qemu-img ' + image + ' ' + str(os.getuid()))
  475. - return True
  476. -
  477. - def docker_p(self):
  478. - try:
  479. - self.docker_version()
  480. - return True
  481. - except subprocess.CalledProcessError:
  482. - return False
  483. -
  484. - def docker_version(self):
  485. - return util.sh("docker --version | "
  486. - " perl -pe 's/.*version (.*),.*/\\1/'")
  487. -
  488. - def docker_build_slave(self):
  489. - if self.args.jenkins_slave not in util.sh('docker images'):
  490. - util.sh('docker build -t ' + self.args.jenkins_slave + " " +
  491. - self.args.libdir + '/jenkins-slave-docker')
  492. - return True
  493. - return False
  494. -
  495. - def docker_build_libguestfs(self):
  496. - if self.args.libguestfs not in util.sh('docker images'):
  497. - util.sh('docker build -t ' + self.args.libguestfs + " " +
  498. - self.args.libdir + '/libguestfs-docker')
  499. - return True
  500. - return False
  501. -
  502. - def docker_destroy_slave(self, name):
  503. - util.sh('docker stop ' + name + ' || true')
  504. - util.sh('docker rm ' + name + ' || true')
  505. - return self.destroy_node(name)
  506. -
  507. - def docker_run_slave(self, name):
  508. - self.docker_build_slave()
  509. - slave_port = self.slave_port(name)
  510. - docker_host_version = util.sh("docker --version | "
  511. - " perl -pe 's/.*version (.*),.*/\\1/'")
  512. - master_port = self.master_port(name)
  513. - script = """
  514. -#!/bin/bash
  515. -usermod --uid {uid} jenkins
  516. -groupmod --gid {docker_group_id} docker
  517. -apt-get install -y lxc-docker-{docker_host_version}
  518. -su - -c '{tunnel}' jenkins
  519. -/usr/sbin/sshd -D -E /var/log/auth.log
  520. - """.format(tunnel=self.tunnel(master_port),
  521. - docker_group_id=grp.getgrnam('docker').gr_gid,
  522. - uid=os.getuid(),
  523. - docker_host_version=docker_host_version)
  524. - open(self.args.jenkins_home + '/setup.sh', 'w').write(script)
  525. - self.fetch_ssh_key(self.args.jenkins_home)
  526. - util.sh('docker stop ' + name + ' || true')
  527. - util.sh('docker rm ' + name + ' || true')
  528. - home = os.path.abspath(self.args.jenkins_home)
  529. - util.sh('docker run -d --name ' + name + ' ' +
  530. - ' -p ' + slave_port + ':22 ' +
  531. - ' -v ' + home + ':/home/jenkins ' +
  532. - ' -v /var/run/docker.sock:/run/docker.sock ' +
  533. - self.args.jenkins_slave + ' ' +
  534. - ' bash /home/jenkins/setup.sh')
  535. - return name
  536. -
  537. - def docker_build_master(self):
  538. - if self.args.jenkins_master not in util.sh('docker images'):
  539. - util.sh('docker build -t ' + self.args.jenkins_master + " " +
  540. - self.args.libdir + '/jenkins-docker')
  541. - return True
  542. - return False
  543. -
  544. - def run_master(self):
  545. - self.docker_build_master()
  546. - home = os.path.abspath(self.args.jenkins_home)
  547. - dotssh = home + '/.ssh'
  548. - if not os.path.exists(dotssh):
  549. - os.makedirs(dotssh, 0o700)
  550. - util.sh('ssh-keygen -N "" -f ' + dotssh + '/id_rsa')
  551. - shutil.copyfile(dotssh + '/id_rsa.pub',
  552. - dotssh + '/authorized_keys')
  553. - os.chmod(dotssh + '/authorized_keys', 0o600)
  554. - util.sh('docker run -d --name ' + self.args.jenkins_master +
  555. - ' -p 50000:50000' +
  556. - ' -p ' + self.args.jenkins_port + ':8080' +
  557. - ' -p ' + self.args.jenkins_ssh_port + ':22' +
  558. - ' -v ' + home + ':/var/jenkins_home' +
  559. - ' ' + self.args.jenkins_master)
  560. -
  561. - def wait_for_credentials():
  562. - timeout = 120
  563. - for t in range(timeout):
  564. - try:
  565. - self.add_credential()
  566. - logging.debug('run_master:wait_for_credentials ' +
  567. - str(self.slave_credential_id()))
  568. - logging.debug('run_master:wait_for_credentials done')
  569. - return
  570. - except requests.exceptions.ConnectionError as e:
  571. - logging.debug('run_master:wait_for_credentials ' + str(e))
  572. - except requests.exceptions.HTTPError as e:
  573. - logging.debug('run_master:wait_for_credentials ' + str(e))
  574. - except NoCredentials as e:
  575. - logging.debug('run_master:wait_for_credentials ' + str(e))
  576. - time.sleep(1)
  577. - raise Exception('failed to add credential')
  578. - wait_for_credentials()
  579. -
  580. - def destroy_master(self):
  581. - util.sh('docker stop ' + self.args.jenkins_master)
  582. - util.sh('docker rm ' + self.args.jenkins_master)
  583. - return True
  584. -
  585. - def add_credential(self):
  586. - cred = (cred_template.
  587. - replace('{KEY_FILE}', '/var/jenkins_home/.ssh/id_rsa').
  588. - replace('{USERNAME}', 'jenkins').
  589. - replace('{DESCRIPTION}', 'for jenkins slaves'))
  590. - r = requests.post(self.get_url() + '/credentials/configSubmit',
  591. - data={'description': 'GLOBAL DESCRIPTION',
  592. - 'name': 'GLOBAL NAME',
  593. - 'json': cred})
  594. - r.raise_for_status()
  595. - return r.text
  596. diff --git a/ceph_workbench/main.py b/ceph_workbench/main.py
  597. index a3c2084..5fa00a0 100644
  598. --- a/ceph_workbench/main.py
  599. +++ b/ceph_workbench/main.py
  600. @@ -18,7 +18,6 @@
  601. # along with this program. If not, see `<http://www.gnu.org/licenses/>`.
  602. #
  603. import argparse
  604. -from ceph_workbench import jenkins
  605. from ceph_workbench import wbbackport
  606. from github2gitlab import main
  607. import logging
  608. @@ -71,15 +70,6 @@ class CephWorkbench:
  609. func=wbbackport.WBBackport,
  610. )
  611. - subparsers.add_parser(
  612. - 'jenkins',
  613. - help='Drive and inspect the Jenkins building Ceph',
  614. - parents=[jenkins.Jenkins.get_parser()],
  615. - add_help=False,
  616. - ).set_defaults(
  617. - func=jenkins.Jenkins,
  618. - )
  619. -
  620. def run(self, argv):
  621. args = self.parser.parse_args(argv)
  622. diff --git a/data_files/jenkins-docker/Dockerfile b/data_files/jenkins-docker/Dockerfile
  623. deleted file mode 100644
  624. index 799b046..0000000
  625. --- a/data_files/jenkins-docker/Dockerfile
  626. +++ /dev/null
  627. @@ -1,13 +0,0 @@
  628. -FROM jenkins
  629. -USER root
  630. -RUN apt-get update && apt-get install -y openssh-server
  631. -# Standard SSH port
  632. -EXPOSE 22
  633. -EXPOSE 50000
  634. -
  635. -RUN sed -i 's|session required pam_loginuid.so|session optional pam_loginuid.so|g' /etc/pam.d/sshd
  636. -RUN mkdir -p /var/run/sshd
  637. -
  638. -RUN ( echo '#!/bin/bash' ; echo /usr/sbin/sshd ; echo chown -R jenkins /var/jenkins_home/.ssh ; echo /usr/local/bin/jenkins.sh ) > /usr/local/bin/jenkins-and-ssh.sh ; chmod +x /usr/local/bin/jenkins-and-ssh.sh
  639. -
  640. -ENTRYPOINT ["/usr/local/bin/jenkins-and-ssh.sh"]
  641. diff --git a/data_files/jenkins-slave-docker/Dockerfile b/data_files/jenkins-slave-docker/Dockerfile
  642. deleted file mode 100644
  643. index 6179bd3..0000000
  644. --- a/data_files/jenkins-slave-docker/Dockerfile
  645. +++ /dev/null
  646. @@ -1,32 +0,0 @@
  647. -# This Dockerfile is used to build an image containing basic stuff to be used as a Jenkins slave build node.
  648. -FROM ubuntu:trusty
  649. -MAINTAINER Loic Dachary <[email protected]>
  650. -
  651. -RUN echo deb http://get.docker.com/ubuntu docker main | tee /etc/apt/sources.list.d/docker.list ; apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
  652. -# Make sure the package repository is up to date.
  653. -RUN apt-get update
  654. -RUN apt-get -y upgrade
  655. -
  656. -RUN apt-get install -y openssh-server git lxc-docker ntp python-pip jq ccache
  657. -# http://stackoverflow.com/questions/27341064/how-do-i-fix-importerror-cannot-import-name-incompleteread
  658. -RUN easy_install -U pip
  659. -RUN pip install ceph-workbench
  660. -RUN sed -i 's|session required pam_loginuid.so|session optional pam_loginuid.so|g' /etc/pam.d/sshd
  661. -RUN mkdir -p /var/run/sshd
  662. -
  663. -# Install JDK 7 (latest edition)
  664. -RUN apt-get install -y openjdk-7-jdk
  665. -
  666. -# Add user jenkins to the image
  667. -RUN adduser --quiet jenkins
  668. -# Set password for the jenkins user (you may want to alter this).
  669. -RUN echo "jenkins:jenkins" | chpasswd
  670. -
  671. -RUN usermod -aG docker jenkins
  672. -
  673. -RUN echo 'jenkins ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/jenkins
  674. -
  675. -# Standard SSH port
  676. -EXPOSE 22
  677. -
  678. -CMD ["/usr/sbin/sshd", "-D"]
  679. diff --git a/docs/index.rst b/docs/index.rst
  680. index 853fc6b..f6ee366 100644
  681. --- a/docs/index.rst
  682. +++ b/docs/index.rst
  683. @@ -25,11 +25,10 @@ development workflow for `Ceph <http://ceph.com>`_.
  684. subcommands:
  685. valid subcommands
  686. - {github2gitlab,backport,jenkins}
  687. + {github2gitlab,backport}
  688. sub-command -h
  689. github2gitlab Mirror a GitHub project to GitLab
  690. backport Backport reports
  691. - jenkins Drive and inspect the Jenkins building Ceph
  692. User Guide
  693. ----------
  694. @@ -43,7 +42,6 @@ instructions for getting the most out of ceph-workbench.
  695. user/intro
  696. user/install
  697. - user/jenkins
  698. Contributor Guide
  699. diff --git a/docs/user/jenkins.rst b/docs/user/jenkins.rst
  700. deleted file mode 100644
  701. index cc323e7..0000000
  702. --- a/docs/user/jenkins.rst
  703. +++ /dev/null
  704. @@ -1,152 +0,0 @@
  705. -.. _jenkins:
  706. -
  707. -Jenkins
  708. -=======
  709. -
  710. -::
  711. -
  712. - +----------------+
  713. - | commit pushed |
  714. - | in ceph |
  715. - +----------------+
  716. - | (0)
  717. - v
  718. - +----------------+
  719. - ssh tunnel | jenkins master | ssh tunnel
  720. - +-------------| ceph job |----------------+
  721. - | (1) +----------------+ |
  722. - | |
  723. - | |
  724. - v v
  725. - +----------+ +----------+
  726. - | jenkins | | jenkins |
  727. - | slave | | slave |
  728. - | (docker) | | (qemu) |
  729. - +----------+ +----------+
  730. - | | (3) |
  731. - (2) | +-------------------------+ (3) |
  732. - | | |
  733. - v v v
  734. - +-----------+ +--------------+ +--------------+
  735. - | container | | container | | qemu i386 |
  736. - | centos 7 | | ubuntu 14.04 | | ubuntu 14.04 |
  737. - +-----------+ +--------------+ +--------------+
  738. - | | |
  739. - v v |
  740. - +-------------------+ +-------------------+ |
  741. - | run-make-check.sh | | run-make-check.sh | |
  742. - +-------------------+ +-------------------+ |
  743. - | | v
  744. - | | +-------------------+
  745. - | +-----------------+ | | run-make-check.sh |
  746. - +---->| jenkins master |<---+ +-------------------+
  747. - | ceph-report | |
  748. - | job |<----------------------+
  749. - +-----------------+
  750. - |
  751. - | (4)
  752. - v
  753. -
  754. -
  755. -.. _`jenkins master`: http://jenkins.ceph.dachary.org/
  756. -.. _`jenkins job`: http://jenkins.ceph.dachary.org/job/ceph/
  757. -.. _`run-make-check.sh`: http://workbench.dachary.org/ceph/ceph/blob/master/run-make-check.sh
  758. -.. _`ceph repository`: http://workbench.dachary.org/ceph/ceph/
  759. -
  760. -A `jenkins job`_ is triggered every time a commit is pushed to the
  761. -`ceph repository`_ and executes the `run-make-check.sh`_ script to
  762. -verify it compiles and passes unit and functional tests.
  763. -
  764. -The `jenkins master`_ delegates the execution of `run-make-check.sh`_
  765. -to `jenkins slaves <http://jenkins.ceph.dachary.org/computer/>`_. If
  766. -the git branch matches a pull request found in the `Ceph official
  767. -repository <http://github.com/ceph/ceph/>`_, a comment is added when
  768. -the run is complete. When there is a failure the full output of the
  769. -script can be browsed to figure out what went wrong.
  770. -
  771. -Jenkins slave CPU & RAM requirements
  772. -------------------------------------
  773. -
  774. -The minimum configuration of a slave is:
  775. -
  776. -- bare metal with 32GB RAM, SSD with 16GB free space, 8 cores OR
  777. -- virtual machine with 32GB RAM, 20GB root disk, 8 cores
  778. -
  779. -It can be hosted on a private IP: it will initiate a tunnel to the
  780. -jenkins master.
  781. -
  782. -Hosting a jenkins slave
  783. ------------------------
  784. -
  785. -In the simplest way to contribute a jenkins slave is when a jenkins
  786. -admin can ssh to it as root. If the slave is behind a firewall
  787. -hosting_private it :ref:`requires two more steps <hosting_private>`.
  788. -
  789. -#. Add the following public key to the ``/root/.ssh/authorized_keys`` file
  790. - of the machine hosting the jenkins slave.
  791. -
  792. - ::
  793. -
  794. - ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDA9mo/xFNhLSCADCjmPQ0MnlTdpIaC8O6vHT5/z7i0boZhLsTe84Gq6sg6qIQY/847/FX52wN6YxMYYjr4478eGa1n84L88WHA4updDT4/LbKzu3aYOVPD6NlkMGKmoJOQazY5z2Mpa/gYHDboZgyyLQ3ApSlM9SCc0xJIJwhGv4uAPjWDzkjCyMCjAOu0NPzJ97uuKgS7e5u1vxL3+6hn7HlIU9wSnA3PEmMUHC8p3f0sHnX5OeZcLwxOAD8v3Q74Lg7yNTc8K0wBuA/G32Tad2QZxFsuSkOyuvhQnWe7dqPL6Jvr20A9wu2A7WVbQ7YaxwngMjB26Pezxg4mFUYV ubuntu@jenkins
  795. -#. Send a mail to `Ceph Development
  796. - <mailto:[email protected]>`_ with the IP of the machine
  797. -
  798. -The jenkins admin will then do the following:
  799. -
  800. -#. ssh -A ``[email protected]``
  801. -#. Create a jenkins user with a home at ``/home/jenkins``
  802. -#. Allow the jenkins user to use docker
  803. -
  804. - ::
  805. -
  806. - $ sudo usermod -aG docker jenkins
  807. -
  808. -#. :ref:`Install <install>` ceph-workbench
  809. -#. Logout
  810. -#. ssh -A ``[email protected]``
  811. -#. Create a new node on the jenkins master, create a jenkins slave container and connect it with the new node.
  812. -
  813. - ::
  814. -
  815. - $ ceph-workbench jenkins \
  816. - --jenkins-url http://jenkins.ceph.dachary.org/ \
  817. - --jenkins-user admin \
  818. - --jenkins-password XXXXXXXX \
  819. - --jenkins-ssh-host [email protected] \
  820. - --jenkins-docker-run-slave new
  821. - slave940
  822. -#. Verify that the newly created node is connected and ready to run
  823. - jobs at http://jenkins.ceph.dachary.org/computer/slave094/
  824. -
  825. -.. _hosting_private:
  826. -
  827. -Hosting a jenkins slave on a private IP
  828. ----------------------------------------
  829. -
  830. -The process for hosting a jenkins slave requires more steps if the
  831. -hosted slave cannot be access from the net:
  832. -
  833. -#. Send a mail to `Ceph Development
  834. - <mailto:[email protected]>`_ with the configuration (RAM,
  835. - disk, CPU) of the machine that could host a jenkins slave.
  836. -#. Wait for the following to be sent:
  837. - - a ssh key
  838. - - the name of the slave (for instance **slave904**)
  839. -#. Create a jenkins user with a home at ``/home/jenkins``
  840. -#. Allow the jenkins user to use docker
  841. -
  842. - ::
  843. -
  844. - sudo usermod -aG docker jenkins
  845. -
  846. -#. :ref:`Install <install>` ceph-workbench
  847. -#. Login as jenkins
  848. -#. Add the ssh key to ``~/.ssh``
  849. -#. Connect to the jenkins master
  850. -
  851. - ::
  852. -
  853. - ceph-workbench jenkins \
  854. - --jenkins-url http://jenkins.ceph.dachary.org/ \
  855. - --jenkins-ssh-host [email protected] \
  856. - --jenkins-docker-run-slave slave904
  857. diff --git a/requirements.txt b/requirements.txt
  858. index 3af59f9..2f75c77 100644
  859. --- a/requirements.txt
  860. +++ b/requirements.txt
  861. @@ -7,3 +7,4 @@ github2gitlab
  862. six>=1.9.0
  863. sphinx>=1.1.3
  864. docutils>=0.11
  865. +twisted>=15.2
  866. diff --git a/tests/jenkins-define-credentials.py b/tests/jenkins-define-credentials.py
  867. deleted file mode 100644
  868. index 66a79f2..0000000
  869. --- a/tests/jenkins-define-credentials.py
  870. +++ /dev/null
  871. @@ -1,46 +0,0 @@
  872. -import requests
  873. -import sys
  874. -
  875. -cred_template = """
  876. -{
  877. - "domainCredentials": {
  878. - "domain": {
  879. - "name": "",
  880. - "description": ""
  881. - },
  882. - "credentials": [
  883. - {
  884. - "scope": "GLOBAL",
  885. - "id": "",
  886. - "username": "{USERNAME}",
  887. - "description": "{DESCRIPTION}",
  888. - "privateKeySource": {
  889. - "value": "1",
  890. - "privateKeyFile": "{KEY_FILE}",
  891. - "stapler-class": "com.cloudbees.jenkins.plugins\
  892. -.sshcredentials.impl.BasicSSHUserPrivateKey$FileOnMasterPrivateKeySource"
  893. - },
  894. - "passphrase": "",
  895. - "stapler-class": "com.cloudbees.jenkins.plugins\
  896. -.sshcredentials.impl.BasicSSHUserPrivateKey",
  897. - "kind": "com.cloudbees.jenkins.plugins\
  898. -.sshcredentials.impl.BasicSSHUserPrivateKey"
  899. - }
  900. - ]
  901. - }
  902. -}
  903. -"""
  904. -
  905. -
  906. -def init(url):
  907. - cred = (cred_template.
  908. - replace('{KEY_FILE}', '/var/jenkins_home/.ssh/id_rsa').
  909. - replace('{USERNAME}', 'ubuntu').
  910. - replace('{DESCRIPTION}', 'for jenkins slaves'))
  911. - r = requests.post(url + '/credentials/configSubmit',
  912. - data={'description': 'GLOBAL DESCRIPTION',
  913. - 'name': 'GLOBAL NAME',
  914. - 'json': cred})
  915. - r.raise_for_status()
  916. -
  917. -init(*sys.argv[1:])
  918. diff --git a/tests/test_jenkins.py b/tests/test_jenkins.py
  919. deleted file mode 100644
  920. index 2782ef9..0000000
  921. --- a/tests/test_jenkins.py
  922. +++ /dev/null
  923. @@ -1,510 +0,0 @@
  924. -# -*- mode: python; coding: utf-8 -*-
  925. -#
  926. -# Copyright (C) 2015 <[email protected]>
  927. -#
  928. -# Author: Loic Dachary <[email protected]>
  929. -#
  930. -# This program is free software: you can redistribute it and/or modify
  931. -# it under the terms of the GNU General Public License as published by
  932. -# the Free Software Foundation, either version 3 of the License, or
  933. -# (at your option) any later version.
  934. -#
  935. -# This program is distributed in the hope that it will be useful,
  936. -# but WITHOUT ANY WARRANTY; without even the implied warranty of
  937. -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  938. -# GNU General Public License for more details.
  939. -#
  940. -# You should have received a copy of the GNU General Public License
  941. -# along with this program. If not, see <http://www.gnu.org/licenses/>.
  942. -#
  943. -import logging
  944. -import os
  945. -import responses
  946. -import shutil
  947. -import subprocess
  948. -import testtools
  949. -import threading
  950. -import time
  951. -
  952. -from ceph_workbench import jenkins
  953. -from ceph_workbench import util
  954. -
  955. -JENKINS = {
  956. - 'ssh-host': '[email protected]',
  957. - 'ssh-port': '5555',
  958. - 'key': 'data/jenkins/.ssh/id_rsa',
  959. - 'host': 'localhost',
  960. - 'port': '11080',
  961. - 'user': 'admin',
  962. - 'password': 'admin',
  963. - 'qemu-image': 'ubuntu-14.04-i386',
  964. -}
  965. -
  966. -logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
  967. - level=logging.DEBUG)
  968. -
  969. -
  970. -def try_a_few_times(command):
  971. - timeout = 120
  972. - for t in range(timeout):
  973. - time.sleep(1)
  974. - try:
  975. - return util.sh(command)
  976. - except subprocess.CalledProcessError:
  977. - pass
  978. - raise Exception(command + ' failed ' + str(timeout) + ' times')
  979. -
  980. -
  981. -class TestJenkinsAll(testtools.TestCase):
  982. -
  983. - @classmethod
  984. - def setUpClass(cls):
  985. - image_dir = 'data/' + JENKINS['qemu-image']
  986. - dotssh = image_dir + '/.ssh'
  987. - if os.path.exists(dotssh):
  988. - shutil.rmtree(dotssh)
  989. - img = image_dir + '/' + JENKINS['qemu-image'] + '.img'
  990. - if os.path.exists(img):
  991. - os.unlink(img)
  992. - home = 'data/jenkins'
  993. - cls.j = jenkins.Jenkins.factory([
  994. - '--jenkins-ssh-host', JENKINS['ssh-host'],
  995. - '--jenkins-ssh-port', JENKINS['ssh-port'],
  996. - '--jenkins-home', home,
  997. - '--jenkins-host', JENKINS['host'],
  998. - '--jenkins-port', JENKINS['port'],
  999. - '--libdir', 'data_files',
  1000. - '--datadir', 'data',
  1001. - '--jenkins-run-master',
  1002. - ])
  1003. - cls.tearDownClass()
  1004. - cls.j.run()
  1005. - try_a_few_times(
  1006. - "ssh -p " + JENKINS['ssh-port'] +
  1007. - " -i " + JENKINS['key'] +
  1008. - " " + JENKINS['ssh-host'] +
  1009. - " ps fauwwwx")
  1010. - # when the cli port 50000 is not yet open, the cli returns a
  1011. - # rather confusing error message:
  1012. - # SEVERE: I/O error in channel Chunked connection ...
  1013. - # https://issues.jenkins-ci.org/browse/JENKINS-23232
  1014. -
  1015. - def wait_for_cli():
  1016. - timeout = 120
  1017. - for t in range(timeout):
  1018. - out = cls.j.run_cli('help')
  1019. - if "create-node" in out:
  1020. - return
  1021. - logging.debug('wait_for_cli ' + out)
  1022. - time.sleep(1)
  1023. - raise Exception('failed to communicate with cli')
  1024. - wait_for_cli()
  1025. -
  1026. - @classmethod
  1027. - def tearDownClass(cls):
  1028. - if os.path.exists(cls.j.get_cli_jar()):
  1029. - os.unlink(cls.j.get_cli_jar())
  1030. - util.sh('docker stop ' + cls.j.args.jenkins_master + ' || true')
  1031. - util.sh('docker rm ' + cls.j.args.jenkins_master + ' || true')
  1032. - util.sh('docker rmi ' + cls.j.args.jenkins_master + ' || true')
  1033. - util.sh('sudo rm -fr data/jenkins')
  1034. -
  1035. - @responses.activate
  1036. - def test_publish_build_to_pull_request(self):
  1037. - pr = '123'
  1038. - build = 'ceph/3'
  1039. - jenkins_url = 'http://jenkins'
  1040. - upstream_build = '12'
  1041. - upstream_url = 'job/ceph'
  1042. - build_report = """
  1043. -{
  1044. - "actions": [
  1045. - {
  1046. - "causes": [
  1047. - {
  1048. - "shortDescription": "Started by upstream project build number 12",
  1049. - "upstreamBuild": {upstream_build},
  1050. - "upstreamProject": "ceph",
  1051. - "upstreamUrl": "{upstream_url}"
  1052. - }
  1053. - ]
  1054. - },
  1055. - {}
  1056. - ],
  1057. - "artifacts": [],
  1058. - "building": false,
  1059. - "description": null,
  1060. - "duration": 3996,
  1061. - "estimatedDuration": 2042,
  1062. - "executor": null,
  1063. - "fullDisplayName": "ceph-report #1",
  1064. - "id": "2015-04-06_08-26-47",
  1065. - "keepLog": false,
  1066. - "number": 1,
  1067. - "result": "SUCCESS",
  1068. - "timestamp": 1428308807127,
  1069. - "url": "http://jenkins/job/ceph-report/1/",
  1070. - "builtOn": "rex003",
  1071. - "changeSet": {
  1072. - "items": [],
  1073. - "kind": null
  1074. - },
  1075. - "culprits": []
  1076. -}
  1077. -""".replace('{upstream_url}',
  1078. - upstream_url).replace('{upstream_build}',
  1079. - upstream_build)
  1080. - responses.add(responses.GET, jenkins_url + '/' + build + '/api/json',
  1081. - body=build_report, status=200,
  1082. - content_type='application/json')
  1083. - os_url = 'http://jenkins/job/ceph/TARGET_OPERATING_SYSTEM'
  1084. - upstream_build_report = """
  1085. -{
  1086. - "actions": [
  1087. - {
  1088. - "causes": [
  1089. - {
  1090. - "shortDescription": "Started by user Ceph Admin",
  1091. - "userId": "admin",
  1092. - "userName": "Ceph Admin"
  1093. - }
  1094. - ]
  1095. - },
  1096. - {},
  1097. - {
  1098. - "buildsByBranchName": {
  1099. - "refs/remotes/origin/pull/123": {
  1100. - "buildNumber": 12,
  1101. - "buildResult": null,
  1102. - "marked": {
  1103. - "SHA1": "23549bfbbdc88c874d9660889450612d2d372b26",
  1104. - "branch": [
  1105. - {
  1106. - "SHA1": "23549bfbbdc88c874d9660889450612d2d372b26",
  1107. - "name": "refs/remotes/origin/pull/123"
  1108. - }
  1109. - ]
  1110. - },
  1111. - "revision": {
  1112. - "SHA1": "23549bfbbdc88c874d9660889450612d2d372b26",
  1113. - "branch": [
  1114. - {
  1115. - "SHA1": "23549bfbbdc88c874d9660889450612d2d372b26",
  1116. - "name": "refs/remotes/origin/pull/123"
  1117. - }
  1118. - ]
  1119. - }
  1120. - }
  1121. - },
  1122. - "lastBuiltRevision": {
  1123. - "SHA1": "23549bfbbdc88c874d9660889450612d2d372b26",
  1124. - "branch": [
  1125. - {
  1126. - "SHA1": "23549bfbbdc88c874d9660889450612d2d372b26",
  1127. - "name": "refs/remotes/origin/pull/123"
  1128. - }
  1129. - ]
  1130. - },
  1131. - "remoteUrls": [
  1132. - "http://workbench.dachary.org/ceph/ceph.git"
  1133. - ],
  1134. - "scmName": ""
  1135. - },
  1136. - {},
  1137. - {}
  1138. - ],
  1139. - "artifacts": [],
  1140. - "building": false,
  1141. - "description": null,
  1142. - "duration": 31117,
  1143. - "estimatedDuration": 1971442,
  1144. - "executor": null,
  1145. - "fullDisplayName": "ceph #12",
  1146. - "id": "2015-04-06_08-26-06",
  1147. - "keepLog": false,
  1148. - "number": 12,
  1149. - "result": "FAILURE",
  1150. - "timestamp": 1428308766532,
  1151. - "url": "http://jenkins/{upstream_url}/{upstream_build}/",
  1152. - "builtOn": "rex003",
  1153. - "changeSet": {
  1154. - "items": [],
  1155. - "kind": "git"
  1156. - },
  1157. - "culprits": [],
  1158. - "runs": [
  1159. - {
  1160. - "number": {upstream_build},
  1161. - "url": "{os_url}=centos-7/{upstream_build}/"
  1162. - },
  1163. - {
  1164. - "number": {upstream_build},
  1165. - "url": "{os_url}=ubuntu-14.04/{upstream_build}/"
  1166. - },
  1167. - {
  1168. - "number": 1,
  1169. - "url": "http://jenkins/job/ceph/system=centos-7/1/"
  1170. - }
  1171. - ]
  1172. -}
  1173. -""".replace('{upstream_url}',
  1174. - upstream_url).replace('{upstream_build}',
  1175. - upstream_build).replace('{os_url}', os_url)
  1176. - responses.add(responses.GET, jenkins_url + '/' +
  1177. - upstream_url + '/' + upstream_build + '/api/json',
  1178. - body=upstream_build_report, status=200,
  1179. - content_type='application/json')
  1180. - build_url = ("http://jenkins/" + upstream_url +
  1181. - "/TARGET_OPERATING_SYSTEM=centos-7/" + upstream_build)
  1182. - os_build_report = """
  1183. -{
  1184. - "actions": [
  1185. - {
  1186. - "lastBuiltRevision": {
  1187. - "SHA1": "23549bfbbdc88c874d9660889450612d2d372b26",
  1188. - "branch": [
  1189. - {
  1190. - "SHA1": "23549bfbbdc88c874d9660889450612d2d372b26",
  1191. - "name": "refs/remotes/origin/pull/123"
  1192. - }
  1193. - ]
  1194. - }
  1195. - }
  1196. - ],
  1197. - "number": {upstream_build},
  1198. - "result": "{result}",
  1199. - "url": "{build_url}"
  1200. -}
  1201. -"""
  1202. - centos_build_report = (os_build_report.
  1203. - replace('{build_url}', build_url).
  1204. - replace('{result}', 'SUCCESS').
  1205. - replace('{upstream_build}', upstream_build))
  1206. - responses.add(responses.GET, build_url + '/api/json',
  1207. - body=centos_build_report, status=200,
  1208. - content_type='application/json')
  1209. - build_url = ("http://jenkins/" + upstream_url +
  1210. - "/TARGET_OPERATING_SYSTEM=ubuntu-14.04/" +
  1211. - upstream_build)
  1212. - ubuntu_build_report = (os_build_report.
  1213. - replace('{build_url}', build_url).
  1214. - replace('{result}', 'FAILURE').
  1215. - replace('{upstream_build}', upstream_build))
  1216. - responses.add(responses.GET, build_url + '/api/json',
  1217. - body=ubuntu_build_report, status=200,
  1218. - content_type='application/json')
  1219. - github_repo = 'my/repo'
  1220. - github_token = 'TOKEN'
  1221. - expected = """\
  1222. -FAILURE: http://jenkins/job/ceph/12/
  1223. -* SUCCESS http://jenkins/job/ceph/TARGET_OPERATING_SYSTEM=centos-7/12/
  1224. -* FAILURE http://jenkins/job/ceph/TARGET_OPERATING_SYSTEM=ubuntu-14.04/12/
  1225. -"""
  1226. - github_url = ('https://api.github.com/repos/' + github_repo +
  1227. - '/issues/' + pr + '/comments?access_token=' +
  1228. - github_token)
  1229. -
  1230. - def request_callback(request):
  1231. - return (200, {}, request.body)
  1232. - responses.add_callback(responses.POST, github_url,
  1233. - callback=request_callback,
  1234. - content_type='application/json',
  1235. - match_querystring=True)
  1236. - j = jenkins.Jenkins.factory([
  1237. - '--publish-build-to-pull-request', build,
  1238. - '--jenkins-host', JENKINS['host'],
  1239. - '--jenkins-port', JENKINS['port'],
  1240. - '--github-repo', github_repo,
  1241. - '--github-token', github_token,
  1242. - ])
  1243. - self.assertEquals(expected, j.run())
  1244. -
  1245. - def test_next_node_name(self):
  1246. - j = jenkins.Jenkins.factory([
  1247. - '--jenkins-host', JENKINS['host'],
  1248. - '--jenkins-port', JENKINS['port'],
  1249. - ])
  1250. - self.assertEquals("slave001", j.next_node_name())
  1251. -
  1252. - def test_slave_credential_id(self):
  1253. - j = jenkins.Jenkins.factory([
  1254. - '--jenkins-host', JENKINS['host'],
  1255. - '--jenkins-port', JENKINS['port'],
  1256. - '--jenkins-user', JENKINS['user'],
  1257. - '--jenkins-password', JENKINS['password'],
  1258. - ])
  1259. - self.assertTrue("-" in j.slave_credential_id())
  1260. -
  1261. - def test_run_cli(self):
  1262. - j = jenkins.Jenkins.factory([
  1263. - '--jenkins-host', JENKINS['host'],
  1264. - '--jenkins-port', JENKINS['port'],
  1265. - ])
  1266. - self.assertIn("create-node", j.run_cli('help'))
  1267. -
  1268. - def test_create_node(self):
  1269. - j = jenkins.Jenkins.factory([
  1270. - '--jenkins-host', JENKINS['host'],
  1271. - '--jenkins-port', JENKINS['port'],
  1272. - '--jenkins-user', JENKINS['user'],
  1273. - '--jenkins-password', JENKINS['password'],
  1274. - ])
  1275. - name = 'NAME005'
  1276. - n = j.create_node(name)
  1277. - self.assertEquals(name, n['displayName'])
  1278. - j.destroy_node(name)
  1279. -
  1280. - def test_build_slave(self):
  1281. - j = jenkins.Jenkins.factory([
  1282. - '--libdir', 'data_files',
  1283. - ])
  1284. - j.docker_build_slave()
  1285. - self.assertIn(j.args.jenkins_slave,
  1286. - util.sh("docker images " + j.args.jenkins_slave))
  1287. -
  1288. - def test_build_libguestfs(self):
  1289. - j = jenkins.Jenkins.factory([
  1290. - '--libdir', 'data_files',
  1291. - ])
  1292. - j.docker_build_libguestfs()
  1293. - self.assertIn(j.args.libguestfs,
  1294. - util.sh("docker images " + j.args.libguestfs))
  1295. -
  1296. - def test_prepare_qemu_img(self):
  1297. - j = jenkins.Jenkins.factory([
  1298. - '--jenkins-ssh-host', JENKINS['ssh-host'],
  1299. - '--jenkins-ssh-key', JENKINS['key'],
  1300. - '--jenkins-ssh-port', JENKINS['ssh-port'],
  1301. - '--jenkins-qemu-image', JENKINS['qemu-image'],
  1302. - '--libdir', 'data_files',
  1303. - '--datadir', 'data',
  1304. - '--jenkins-qemu-prepare',
  1305. - ])
  1306. - j.run()
  1307. - self.assertTrue(os.path.exists('data/' + JENKINS['qemu-image']))
  1308. - self.assertTrue(os.path.exists('data/' + JENKINS['qemu-image'] +
  1309. - '/.ssh/id_rsa'))
  1310. -
  1311. - def test_qemu_run_jenkins_slave(self):
  1312. - options = [
  1313. - '--jenkins-ssh-host', JENKINS['ssh-host'],
  1314. - '--jenkins-ssh-key', JENKINS['key'],
  1315. - '--jenkins-ssh-port', JENKINS['ssh-port'],
  1316. - '--jenkins-host', JENKINS['host'],
  1317. - '--jenkins-port', JENKINS['port'],
  1318. - '--jenkins-user', JENKINS['user'],
  1319. - '--jenkins-password', JENKINS['password'],
  1320. - '--jenkins-qemu-image', JENKINS['qemu-image'],
  1321. - '--libdir', 'data_files',
  1322. - '--datadir', 'data',
  1323. - ]
  1324. - j = jenkins.Jenkins.factory(options)
  1325. - name = j.next_node_name()
  1326. - master_port = str(j.master_port(name))
  1327. - slave_port = str(j.slave_port(name))
  1328. - # this is implicit when calling qemu_run_slave
  1329. - # but we call it outside of the thread so that it
  1330. - # blocks the test because it may take a long time
  1331. - # and be confused with something going wrong in
  1332. - # the thread
  1333. - j.qemu_get_img()
  1334. -
  1335. - def kvm_start():
  1336. - other = jenkins.Jenkins.factory(options + [
  1337. - '--jenkins-qemu-run-slave', 'new',
  1338. - ])
  1339. - other.qemu_run_slave(name)
  1340. - kvm = threading.Thread(target=kvm_start)
  1341. - kvm.start()
  1342. -
  1343. - known_hosts = j.ignore_known_hosts()
  1344. - # ssh to the slave and give time to kvm launch
  1345. - via_slave_cmdline = try_a_few_times(
  1346. - "ssh -p " + slave_port +
  1347. - " " + known_hosts +
  1348. - " -i " + JENKINS['key'] +
  1349. - " jenkins@localhost cat /proc/cmdline")
  1350. - self.assertIn(master_port, via_slave_cmdline)
  1351. - util.sh("ssh -p " + slave_port +
  1352. - " " + known_hosts +
  1353. - " -i " + JENKINS['key'] +
  1354. - " jenkins@localhost ps fauwwx")
  1355. - # ssh to the slave via the master
  1356. - via_master_cmdline = try_a_few_times(
  1357. - "ssh -p " + JENKINS['ssh-port'] +
  1358. - " " + known_hosts +
  1359. - " -i " + JENKINS['key'] +
  1360. - " " + JENKINS['ssh-host'] +
  1361. - " ssh -p " + master_port +
  1362. - " " + known_hosts +
  1363. - " localhost cat /proc/cmdline")
  1364. - self.assertIn('ds=nocloud-net', via_master_cmdline)
  1365. - # halt the kvm process and expect the kvm thread to return
  1366. - j.qemu_destroy_slave(name)
  1367. - kvm.join()
  1368. - self.assertFalse(j.find_node(name))
  1369. -
  1370. - def test_docker_run_jenkins_slave(self):
  1371. - options = [
  1372. - '--jenkins-ssh-host', JENKINS['ssh-host'],
  1373. - '--jenkins-ssh-key', JENKINS['key'],
  1374. - '--jenkins-ssh-port', JENKINS['ssh-port'],
  1375. - '--jenkins-host', JENKINS['host'],
  1376. - '--jenkins-port', JENKINS['port'],
  1377. - '--jenkins-user', JENKINS['user'],
  1378. - '--jenkins-password', JENKINS['password'],
  1379. - '--libdir', 'data_files',
  1380. - '--jenkins-home', 'data_files',
  1381. - ]
  1382. - j = jenkins.Jenkins.factory(options + [
  1383. - '--jenkins-docker-run-slave', 'new',
  1384. - ])
  1385. - name = j.run()
  1386. - self.assertTrue(j.find_node(name))
  1387. - j.docker_destroy_slave(name)
  1388. - self.assertFalse(j.find_node(name))
  1389. -
  1390. - name = 'slave010'
  1391. - j = jenkins.Jenkins.factory(options + [
  1392. - '--jenkins-docker-run-slave', name,
  1393. - ])
  1394. - self.assertEquals(name, j.run())
  1395. - j.docker_destroy_slave(name)
  1396. - self.assertFalse(j.find_node(name))
  1397. -
  1398. -
  1399. -class TestJenkinsMaster(testtools.TestCase):
  1400. -
  1401. - def test_build_master(self):
  1402. - j = jenkins.Jenkins.factory([
  1403. - '--libdir', 'data_files',
  1404. - ])
  1405. - util.sh('docker rmi ' + j.args.jenkins_master + ' || true')
  1406. - j.docker_build_master()
  1407. - self.assertIn(j.args.jenkins_master,
  1408. - util.sh("docker images " + j.args.jenkins_master))
  1409. -
  1410. - def test_run_master(self):
  1411. - home = 'data/jenkins'
  1412. - j = jenkins.Jenkins.factory([
  1413. - '--jenkins-ssh-host', JENKINS['ssh-host'],
  1414. - '--jenkins-ssh-port', JENKINS['ssh-port'],
  1415. - '--jenkins-home', home,
  1416. - '--jenkins-host', JENKINS['host'],
  1417. - '--jenkins-port', JENKINS['port'],
  1418. - '--libdir', 'data_files',
  1419. - '--datadir', 'data',
  1420. - '--jenkins-run-master',
  1421. - ])
  1422. - util.sh('docker stop ' + j.args.jenkins_master + ' || true')
  1423. - util.sh('docker rm ' + j.args.jenkins_master + ' || true')
  1424. - util.sh('docker rmi ' + j.args.jenkins_master + ' || true')
  1425. - util.sh('sudo rm -fr data/jenkins')
  1426. - j.run()
  1427. - out = try_a_few_times(
  1428. - "ssh -p " + JENKINS['ssh-port'] +
  1429. - " -i " + JENKINS['key'] +
  1430. - " " + JENKINS['ssh-host'] +
  1431. - " ps fauwwwx")
  1432. - self.assertIn("jenkins.war", out)
  1433. - self.assertTrue(j.destroy_master())