~ubuntu-branches/ubuntu/trusty/python-jenkinsapi/trusty-proposed

« back to all changes in this revision

Viewing changes to jenkinsapi_utils/jenkins_launcher.py

  • Committer: Package Import Robot
  • Author(s): Al Stone
  • Date: 2014-01-06 18:12:26 UTC
  • mfrom: (1.1.2)
  • Revision ID: package-import@ubuntu.com-20140106181226-j4n5l4idgxghlvwg
Tags: 0.2.16-1
* Update to latest upstream.
* Closes: #725589 -- FTBS due to unexpected upstream changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import os
 
2
import time
 
3
import Queue
 
4
import random
 
5
import shutil
 
6
import logging
 
7
import datetime
 
8
import tempfile
 
9
import requests
 
10
import threading
 
11
import subprocess
 
12
import pkg_resources
 
13
 
 
14
from jenkinsapi.jenkins import Jenkins
 
15
from jenkinsapi.custom_exceptions import JenkinsAPIException
 
16
 
 
17
log = logging.getLogger(__name__)
 
18
 
 
19
 
 
20
class FailedToStart(Exception):
 
21
    pass
 
22
 
 
23
 
 
24
class TimeOut(Exception):
 
25
    pass
 
26
 
 
27
 
 
28
class StreamThread(threading.Thread):
 
29
 
 
30
    def __init__(self, name, q, stream, fn_log):
 
31
        threading.Thread.__init__(self)
 
32
        self.name = name
 
33
        self.q = q
 
34
        self.stream = stream
 
35
        self.fn_log = fn_log
 
36
 
 
37
    def run(self):
 
38
        log.info("Starting %s", self.name)
 
39
 
 
40
        while True:
 
41
            line = self.stream.readline()
 
42
            if line:
 
43
                self.fn_log(line.rstrip())
 
44
                self.q.put((self.name, line))
 
45
            else:
 
46
                break
 
47
        self.q.put((self.name, None))
 
48
 
 
49
 
 
50
class JenkinsLancher(object):
 
51
 
 
52
    """
 
53
    Launch jenkins
 
54
    """
 
55
    JENKINS_WAR_URL = "http://mirrors.jenkins-ci.org/war/latest/jenkins.war"
 
56
 
 
57
    def __init__(self, war_path, plugin_urls=None):
 
58
        self.war_path = war_path
 
59
        self.war_directory, self.war_filename = os.path.split(self.war_path)
 
60
        self.jenkins_home = tempfile.mkdtemp(prefix='jenkins-home-')
 
61
        self.jenkins_process = None
 
62
        self.q = Queue.Queue()
 
63
        self.plugin_urls = plugin_urls or []
 
64
        self.http_port = random.randint(9000, 10000)
 
65
 
 
66
    def update_war(self):
 
67
        os.chdir(self.war_directory)
 
68
        if os.path.exists(self.war_path):
 
69
            log.info("We already have the War file...")
 
70
        else:
 
71
            log.info("Redownloading Jenkins")
 
72
            subprocess.check_call('./get-jenkins-war.sh')
 
73
 
 
74
    def update_config(self):
 
75
        config_dest = os.path.join(self.jenkins_home, 'config.xml')
 
76
        config_dest_file = open(config_dest, 'w')
 
77
        config_source = pkg_resources.resource_string('jenkinsapi_tests.systests', 'config.xml')
 
78
        config_dest_file.write(config_source.encode('UTF-8'))
 
79
 
 
80
    def install_plugins(self):
 
81
        for i, url in enumerate(self.plugin_urls):
 
82
            self.install_plugin(url, i)
 
83
 
 
84
    def install_plugin(self, hpi_url, i):
 
85
        plugin_dir = os.path.join(self.jenkins_home, 'plugins')
 
86
        if not os.path.exists(plugin_dir):
 
87
            os.mkdir(plugin_dir)
 
88
 
 
89
        log.info("Downloading %s", hpi_url)
 
90
        log.info("Plugins will be installed in '%s'" % plugin_dir)
 
91
        # FIXME: This is kinda ugly but works
 
92
        filename = "plugin_%s.hpi" % i
 
93
        plugin_path = os.path.join(plugin_dir, filename)
 
94
        with open(plugin_path, 'wb') as h:
 
95
            request = requests.get(hpi_url)
 
96
            h.write(request.content)
 
97
 
 
98
    def stop(self):
 
99
        log.info("Shutting down jenkins.")
 
100
        self.jenkins_process.terminate()
 
101
        self.jenkins_process.wait()
 
102
        shutil.rmtree(self.jenkins_home)
 
103
 
 
104
    def block_until_jenkins_ready(self, timeout):
 
105
        start_time = datetime.datetime.now()
 
106
        timeout_time = start_time + datetime.timedelta(seconds=timeout)
 
107
 
 
108
        while True:
 
109
            try:
 
110
                Jenkins('http://localhost:8080')
 
111
                log.info('Jenkins is finally ready for use.')
 
112
            except JenkinsAPIException:
 
113
                log.info('Jenkins is not yet ready...')
 
114
            if datetime.datetime.now() > timeout_time:
 
115
                raise TimeOut('Took too long for Jenkins to become ready...')
 
116
            time.sleep(5)
 
117
 
 
118
    def start(self, timeout=60):
 
119
        self.update_war()
 
120
        self.update_config()
 
121
        self.install_plugins()
 
122
 
 
123
        os.environ['JENKINS_HOME'] = self.jenkins_home
 
124
        os.chdir(self.war_directory)
 
125
 
 
126
        jenkins_command = ['java', '-jar', self.war_filename, 
 
127
            '--httpPort=%d' % self.http_port]
 
128
 
 
129
        log.info("About to start Jenkins...")
 
130
        log.info("%s> %s", os.getcwd(), " ".join(jenkins_command))
 
131
        self.jenkins_process = subprocess.Popen(
 
132
            jenkins_command, stdin=subprocess.PIPE,
 
133
            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
134
 
 
135
        threads = [
 
136
            StreamThread('out', self.q, self.jenkins_process.stdout, log.info),
 
137
            StreamThread('err', self.q, self.jenkins_process.stderr, log.warn)
 
138
        ]
 
139
 
 
140
        # Start the threads
 
141
        for t in threads:
 
142
            t.start()
 
143
 
 
144
        while True:
 
145
            try:
 
146
                streamName, line = self.q.get(block=True, timeout=timeout)
 
147
            except Queue.Empty:
 
148
                log.warn("Input ended unexpectedly")
 
149
                break
 
150
            else:
 
151
                if line:
 
152
                    if 'Failed to initialize Jenkins' in line:
 
153
                        raise FailedToStart(line)
 
154
 
 
155
                    if 'Invalid or corrupt jarfile' in line:
 
156
                        raise FailedToStart(line)
 
157
 
 
158
                    if 'is fully up and running' in line:
 
159
                        log.info(line)
 
160
                        return
 
161
                else:
 
162
                    log.warn('Stream %s has terminated', streamName)
 
163
 
 
164
        self.block_until_jenkins_ready(timeout)
 
165
 
 
166
 
 
167
if __name__ == '__main__':
 
168
    logging.basicConfig()
 
169
    logging.getLogger('').setLevel(logging.INFO)
 
170
 
 
171
    log.info("Hello!")
 
172
 
 
173
    jl = JenkinsLancher(
 
174
        '/home/sal/workspace/jenkinsapi/src/jenkinsapi_tests/systests/jenkins.war'
 
175
    )
 
176
    jl.start()
 
177
    log.info("Jenkins was launched...")
 
178
 
 
179
    time.sleep(30)
 
180
 
 
181
    log.info("...now to shut it down!")
 
182
    jl.stop()