~sinzui/juju-ci-tools/cloudsigma-lib

915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
1
#!/usr/bin/env python
994.1.2 by seman.said at canonical
Removed upload_heterogeneous() from assess_heterogeneous_control.py.
2
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
3
from __future__ import print_function
4
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
5
from argparse import ArgumentParser
6
from ConfigParser import ConfigParser
7
import json
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
8
import os
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
9
import sys
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
10
from time import sleep
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
11
import urlparse
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
12
13
from boto.s3.connection import S3Connection
14
import requests
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
15
from requests.auth import HTTPBasicAuth
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
16
915.2.3 by seman.said at canonical
Updated code per review suggestion
17
from jujuci import(
976.1.1 by Curtis Hovey
Use jujuci to get build and job data because it works on all OSes and series.
18
    get_build_data,
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
19
    add_credential_args,
20
    get_credentials,
976.1.1 by Curtis Hovey
Use jujuci to get build and job data because it works on all OSes and series.
21
    get_job_data,
915.2.3 by seman.said at canonical
Updated code per review suggestion
22
    JENKINS_URL,
23
)
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
24
from utility import until_timeout
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
25
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
26
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
27
__metaclass__ = type
28
29
915.2.3 by seman.said at canonical
Updated code per review suggestion
30
class JenkinsBuild:
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
31
    """
32
    Retrieves Jenkins build information
33
    """
34
976.1.4 by Curtis Hovey
Removed the need for the Jenkins object.
35
    def __init__(self, credentials, job_name, jenkins_url, build_info):
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
36
        """
37
        :param credentials: Jenkins credentials
38
        :param job_name:  Jenkins job name
976.1.4 by Curtis Hovey
Removed the need for the Jenkins object.
39
        :param jenkins_url: Jenkins server URL
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
40
        :param build_info: Jenkins build info
41
        :return: None
42
        """
43
        self.credentials = credentials
976.1.4 by Curtis Hovey
Removed the need for the Jenkins object.
44
        self.jenkins_url = jenkins_url
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
45
        self.job_name = job_name
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
46
        self.build_info = build_info
47
48
    @classmethod
49
    def factory(cls, credentials, job_name, build_number=None, url=None):
915.2.3 by seman.said at canonical
Updated code per review suggestion
50
        """
51
        :param credentials: Jenkins credentials
52
        :param job_name: Jenkins job name
53
        :param build_number: Jenkins build number
54
        :param url: Jenkins url
55
        :return:
56
        """
57
        url = url or JENKINS_URL
976.1.4 by Curtis Hovey
Removed the need for the Jenkins object.
58
        build_info = (get_build_data(url, credentials, job_name, build_number)
915.2.3 by seman.said at canonical
Updated code per review suggestion
59
                      if build_number else None)
976.1.4 by Curtis Hovey
Removed the need for the Jenkins object.
60
        return cls(credentials, job_name, url, build_info)
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
61
915.2.3 by seman.said at canonical
Updated code per review suggestion
62
    def get_build_info(self, build_number=None):
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
63
        """
64
        Gets build info from the Jenkins server
65
        :rtype: dict
66
        """
915.2.3 by seman.said at canonical
Updated code per review suggestion
67
        build_number = build_number or self.get_build_number()
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
68
        self.build_info = get_build_data(
976.1.4 by Curtis Hovey
Removed the need for the Jenkins object.
69
            self.jenkins_url, self.credentials, self.job_name, build_number)
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
70
        return self.build_info
71
72
    def is_build_completed(self):
73
        """Check if the build is completed and return boolean."""
996.3.2 by seman.said at canonical
Added a unit test for the upload_by_build_number method.
74
        build_info = self.get_build_info()
75
        return not build_info['building']
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
76
77
    @property
78
    def result(self):
79
        """
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
80
        Returns the test result string
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
81
        :return: SUCCESS, FAILURE, ABORTED, NOT_BUILT, SUCCESS, UNSTABLE ...
82
        :rtype: str
83
        """
84
        return self.build_info.get('result')
85
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
86
    def get_console_text(self):
87
        """
88
        Return Jenkins build console log
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
89
        :rtype: str
90
        """
91
        log_url = urlparse.urljoin(self.build_info['url'], 'consoleText')
92
        return requests.get(
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
93
            log_url, auth=HTTPBasicAuth(
94
                self.credentials.user, self.credentials.password)).text
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
95
994.1.3 by seman.said at canonical
Updated to upload lastCompletedBuild instead of lastBuild number.
96
    def get_last_completed_build_number(self):
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
97
        """
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
98
        Returns latest Jenkins build number
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
99
        :rtype: int
100
        """
976.1.1 by Curtis Hovey
Use jujuci to get build and job data because it works on all OSes and series.
101
        job_info = get_job_data(
976.1.4 by Curtis Hovey
Removed the need for the Jenkins object.
102
            self.jenkins_url, self.credentials, self.job_name)
994.1.3 by seman.said at canonical
Updated to upload lastCompletedBuild instead of lastBuild number.
103
        return job_info['lastCompletedBuild']['number']
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
104
105
    def artifacts(self):
106
        """
107
        Returns the filename and the content of artifacts
108
        :return: filename and artifacts content
109
        :rtype: tuple
110
        """
111
        relative_paths = [(x['relativePath'], x['fileName']) for x in
112
                          self.build_info['artifacts']]
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
113
        auth = HTTPBasicAuth(self.credentials.user, self.credentials.password)
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
114
        for path, filename in relative_paths:
115
            url = self._get_artifact_url(path)
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
116
            content = requests.get(url, auth=auth).content
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
117
            yield filename, content
118
119
    def _get_artifact_url(self, relative_path):
120
        """
121
        :return: List of artifact URLs
122
        :rtype: list
123
        """
124
        return self.build_info['url'] + 'artifact/' + relative_path
125
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
126
    def get_build_number(self):
915.2.3 by seman.said at canonical
Updated code per review suggestion
127
        return self.build_info.get('number')
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
128
129
    def set_build_number(self, build_number):
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
130
        self.get_build_info(build_number)
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
131
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
132
133
class S3:
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
134
    """
135
    Used to store an object in S3
136
    """
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
137
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
138
    def __init__(self, directory, access_key, secret_key, conn, bucket):
139
        self.dir = directory
140
        self.access_key = access_key
141
        self.secret_key = secret_key
142
        self.conn = conn
143
        self.bucket = bucket
144
145
    @classmethod
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
146
    def factory(cls, bucket, directory):
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
147
        access_key, secret_key = get_s3_access()
148
        conn = S3Connection(access_key, secret_key)
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
149
        bucket = conn.get_bucket(bucket)
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
150
        return cls(directory, access_key, secret_key, conn, bucket)
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
151
152
    def store(self, filename, data, headers=None):
153
        """
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
154
        Stores an object in S3.
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
155
        :param filename: filename of the object
156
        :param data: The content to be stored in S3
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
157
        :rtype: bool
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
158
        """
159
        if not data:
160
            return False
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
161
        path = os.path.join(self.dir, filename)
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
162
        key = self.bucket.new_key(path)
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
163
        # This will store the data.
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
164
        key.set_contents_from_string(data, headers=headers)
165
        return True
166
167
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
168
class S3Uploader:
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
169
    """
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
170
    Uploads the result of a Jenkins job to S3.
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
171
    """
172
915.2.3 by seman.said at canonical
Updated code per review suggestion
173
    def __init__(self, s3, jenkins_build):
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
174
        self.s3 = s3
915.2.3 by seman.said at canonical
Updated code per review suggestion
175
        self.jenkins_build = jenkins_build
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
176
177
    @classmethod
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
178
    def factory(cls, credentials, jenkins_job, build_number, bucket,
179
                directory):
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
180
        """
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
181
        Creates S3Uploader.
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
182
        :param credentials: Jenkins credential
1056.1.2 by John George
Unit test updates for upload_jenkins_job.
183
        :param jenkins_job: Jenkins job name
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
184
        :param build_number: Jenkins build number
1056.1.2 by John George
Unit test updates for upload_jenkins_job.
185
        :param bucket: S3 bucket name
186
        :param directory: S3 directory name
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
187
        :rtype: S3Uploader
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
188
        """
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
189
        s3 = S3.factory(bucket, directory)
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
190
        build_number = int(build_number) if build_number else build_number
915.2.3 by seman.said at canonical
Updated code per review suggestion
191
        jenkins_build = JenkinsBuild.factory(
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
192
            credentials=credentials, job_name=jenkins_job,
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
193
            build_number=build_number)
915.2.3 by seman.said at canonical
Updated code per review suggestion
194
        return cls(s3, jenkins_build)
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
195
196
    def upload(self):
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
197
        """Uploads Jenkins job results, console logs and artifacts to S3.
198
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
199
        :return: None
200
        """
201
        self.upload_test_results()
202
        self.upload_console_log()
203
        self.upload_artifacts()
204
996.3.2 by seman.said at canonical
Added a unit test for the upload_by_build_number method.
205
    def upload_by_build_number(self, build_number=None, pause_time=120,
206
                               timeout=600):
207
        """
208
        Upload build_number's test result.
209
210
        :param build_number:
211
        :param pause_time: Pause time in seconds between polling.
212
        :param timeout: Timeout in seconds.
213
        :return: None
214
        """
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
215
        build_number = build_number or os.getenv('BUILD_NUMBER')
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
216
        if not build_number:
217
            raise ValueError('Build number is not set')
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
218
        self.jenkins_build.set_build_number(build_number)
996.3.2 by seman.said at canonical
Added a unit test for the upload_by_build_number method.
219
        for _ in until_timeout(timeout):
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
220
            if self.jenkins_build.is_build_completed():
221
                break
996.3.2 by seman.said at canonical
Added a unit test for the upload_by_build_number method.
222
            sleep(pause_time)
996.3.1 by seman.said at canonical
Ensure Jenkins build is completed before uploading compatibility test result by a build number.
223
        else:
224
            raise Exception("Build fails to complete: {}".format(build_number))
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
225
        self.upload()
226
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
227
    def upload_all_test_results(self):
228
        """
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
229
        Uploads all the test results to S3. It starts with the build_number 1
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
230
        :return: None
231
        """
994.1.3 by seman.said at canonical
Updated to upload lastCompletedBuild instead of lastBuild number.
232
        latest_build_num = self.jenkins_build.get_last_completed_build_number()
954.1.11 by Curtis Hovey
Hush py3 lint.
233
        for build_number in range(1, latest_build_num + 1):
915.2.3 by seman.said at canonical
Updated code per review suggestion
234
            self.jenkins_build.set_build_number(build_number)
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
235
            self.upload()
236
994.1.3 by seman.said at canonical
Updated to upload lastCompletedBuild instead of lastBuild number.
237
    def upload_last_completed_test_result(self):
994.1.1 by seman.said at canonical
Updated upload_hetero_control.py to upload the latest test results to S3.
238
        """Upload the latest test result to S3."""
994.1.3 by seman.said at canonical
Updated to upload lastCompletedBuild instead of lastBuild number.
239
        latest_build_num = self.jenkins_build.get_last_completed_build_number()
994.1.1 by seman.said at canonical
Updated upload_hetero_control.py to upload the latest test results to S3.
240
        self.jenkins_build.set_build_number(latest_build_num)
241
        self.upload()
242
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
243
    def upload_test_results(self):
244
        filename = self._create_filename('result-results.json')
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
245
        headers = {"Content-Type": "application/json"}
915.2.3 by seman.said at canonical
Updated code per review suggestion
246
        self.s3.store(filename, json.dumps(
1091.2.1 by John George
Indent json uploaded by upload_jenkins_job.py, to make it easier to read.
247
            self.jenkins_build.get_build_info(), indent=4), headers=headers)
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
248
249
    def upload_console_log(self):
250
        filename = self._create_filename('console-consoleText.txt')
915.2.3 by seman.said at canonical
Updated code per review suggestion
251
        headers = {"Content-Type": "text/plain; charset=utf8"}
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
252
        self.s3.store(
915.2.3 by seman.said at canonical
Updated code per review suggestion
253
            filename, self.jenkins_build.get_console_text(), headers=headers)
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
254
255
    def upload_artifacts(self):
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
256
        headers = {"Content-Type": "application/octet-stream"}
915.2.3 by seman.said at canonical
Updated code per review suggestion
257
        for filename, content in self.jenkins_build.artifacts():
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
258
            filename = self._create_filename('log-' + filename)
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
259
            self.s3.store(filename, content, headers=headers)
915.2.1 by seman.said at canonical
Added code to upload the result of Heterogeneous Control test to S3
260
261
    def _create_filename(self, filename):
262
        """
263
        Creates filename based on the combination of the job ID and the
264
        filename
265
        :return: Filename
266
        :rtype: str
267
        """
915.2.3 by seman.said at canonical
Updated code per review suggestion
268
        return str(self.jenkins_build.get_build_number()) + '-' + filename
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
269
270
271
def get_s3_access():
272
    """
273
    Return S3 access and secret keys
274
    """
275
    s3cfg_path = os.path.join(
276
        os.getenv('HOME'), 'cloud-city/juju-qa.s3cfg')
915.2.3 by seman.said at canonical
Updated code per review suggestion
277
    config = ConfigParser()
915.2.6 by seman.said at canonical
Updated comp-test per review suggestion.
278
    config.read(s3cfg_path)
279
    access_key = config.get('default', 'access_key')
280
    secret_key = config.get('default', 'secret_key')
915.2.5 by seman.said at canonical
Updated comp-test per review suggestion.
281
    return access_key, secret_key
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
282
283
1056.1.2 by John George
Unit test updates for upload_jenkins_job.
284
def get_args(argv=None):
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
285
    parser = ArgumentParser()
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
286
    parser.add_argument('jenkins_job', help="Jenkins job name.")
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
287
    parser.add_argument(
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
288
        'build_number', default=None,
289
        help="Build to upload, can be a number, 'all' or 'latest'.")
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
290
    parser.add_argument('s3_bucket', help="S3 bucket name to store files.")
291
    parser.add_argument(
292
        's3_directory',
293
        help="Directory under the bucket name to store files.")
294
    add_credential_args(parser)
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
295
    args = parser.parse_args(argv)
296
    args.all = False
297
    args.latest = False
298
    if args.build_number == 'all':
299
        args.all = True
300
        args.build_number = None
301
    if args.build_number == 'latest':
302
        args.latest = True
303
        args.build_number = None
304
    if args.build_number:
305
        args.build_number = int(args.build_number)
306
    return args
1056.1.2 by John George
Unit test updates for upload_jenkins_job.
307
308
309
def main(argv=None):
310
    args = get_args(argv)
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
311
    cred = get_credentials(args)
312
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
313
    uploader = S3Uploader.factory(
1056.1.1 by John George
Make upload_hetero_control.py generic for any jenkins job.
314
        cred, args.jenkins_job, args.build_number, args.s3_bucket,
315
        args.s3_directory)
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
316
    if args.build_number:
994.1.1 by seman.said at canonical
Updated upload_hetero_control.py to upload the latest test results to S3.
317
        print('Uploading build number {:d}.'.format(args.build_number))
1056.1.2 by John George
Unit test updates for upload_jenkins_job.
318
        uploader.upload()
915.2.2 by seman.said at canonical
Added unit test and updated code to upload compatibility test results to S3
319
    elif args.all:
994.1.1 by seman.said at canonical
Updated upload_hetero_control.py to upload the latest test results to S3.
320
        print('Uploading all test results.')
1056.1.2 by John George
Unit test updates for upload_jenkins_job.
321
        uploader.upload_all_test_results()
994.1.1 by seman.said at canonical
Updated upload_hetero_control.py to upload the latest test results to S3.
322
    elif args.latest:
323
        print('Uploading the latest test result.')
1056.1.4 by John George
Take a number, 'all' or 'latest' as the second positional parameter.
324
        print('WARNING: latest can be a moving target.')
1056.1.2 by John George
Unit test updates for upload_jenkins_job.
325
        uploader.upload_last_completed_test_result()
326
327
if __name__ == '__main__':
328
    sys.exit(main(sys.argv[1:]))