Dennis Dmitriev | 3ec2e53 | 2018-06-08 04:33:34 +0300 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | |
| 3 | import argparse |
| 4 | import os |
| 5 | import sys |
| 6 | |
Dennis Dmitriev | 13e804b | 2018-10-09 19:25:14 +0300 | [diff] [blame] | 7 | from devops import error |
Dennis Dmitriev | 3ec2e53 | 2018-06-08 04:33:34 +0300 | [diff] [blame] | 8 | import json |
| 9 | |
| 10 | sys.path.append(os.getcwd()) |
| 11 | try: |
| 12 | from tcp_tests.managers.jenkins.client import JenkinsClient |
| 13 | except ImportError: |
| 14 | print("ImportError: Run the application from the tcp-qa directory or " |
| 15 | "set the PYTHONPATH environment variable to directory which contains" |
| 16 | " ./tcp_tests") |
| 17 | sys.exit(1) |
| 18 | |
| 19 | |
| 20 | EXIT_CODES = { |
| 21 | "SUCCESS": 0, |
| 22 | # 1 - python runtime execution error |
| 23 | # 2 - job unknown status |
| 24 | "FAILURE": 3, |
| 25 | "UNSTABLE": 4, |
| 26 | "ABORTED": 5, |
| 27 | "DISABLED": 6 |
| 28 | # 10 - invalid cli options |
| 29 | } |
| 30 | |
| 31 | |
| 32 | def load_params(): |
| 33 | """ |
| 34 | Parse CLI arguments and environment variables |
| 35 | |
| 36 | Returns: ArgumentParser instance |
| 37 | """ |
| 38 | env_host = os.environ.get('JENKINS_URL', None) |
| 39 | env_username = os.environ.get('JENKINS_USER', None) |
| 40 | env_password = os.environ.get('JENKINS_PASS', None) |
| 41 | env_start_timeout = os.environ.get('JENKINS_START_TIMEOUT', 1800) |
| 42 | env_build_timeout = os.environ.get('JENKINS_BUILD_TIMEOUT', 3600 * 4) |
| 43 | |
| 44 | parser = argparse.ArgumentParser(description=( |
| 45 | 'Host, username and password may be specified either by the command ' |
| 46 | 'line arguments or using environment variables: JENKINS_URL, ' |
| 47 | 'JENKINS_USER, JENKINS_PASS, JENKINS_START_TIMEOUT, ' |
| 48 | 'JENKINS_BUILD_TIMEOUT. \nCommand line arguments have the highest ' |
| 49 | 'priority, after that the environment variables are used as defaults.' |
| 50 | )) |
| 51 | parser.add_argument('--host', |
| 52 | metavar='JENKINS_URL', |
| 53 | help='Jenkins Host', |
| 54 | default=env_host) |
| 55 | parser.add_argument('--username', |
| 56 | metavar='JENKINS_USER', |
| 57 | help='Jenkins Username', default=env_username) |
| 58 | parser.add_argument('--password', |
| 59 | metavar='JENKINS_PASS', |
| 60 | help='Jenkins Password or API token', |
| 61 | default=env_password) |
| 62 | parser.add_argument('--start-timeout', |
| 63 | metavar='JENKINS_START_TIMEOUT', |
| 64 | help='Timeout waiting until build is started', |
| 65 | default=env_start_timeout, |
| 66 | type=int) |
| 67 | parser.add_argument('--build-timeout', |
| 68 | metavar='JENKINS_BUILD_TIMEOUT', |
| 69 | help='Timeout waiting until build is finished', |
| 70 | default=env_build_timeout, |
| 71 | type=int) |
| 72 | parser.add_argument('--job-name', |
| 73 | help='Jenkins job name to run', |
| 74 | default=None) |
| 75 | parser.add_argument('--job-parameters', |
| 76 | metavar='json-dict', |
| 77 | help=('Job parameters to use instead of default ' |
| 78 | 'values, as a json string, for example: ' |
| 79 | '--job-parameters=\'{"SALT_MASTER_URL": ' |
| 80 | '"http://localhost:6969"}\''), |
| 81 | default={}, type=json.loads) |
| 82 | parser.add_argument('--job-output-prefix', |
| 83 | help=('Jenkins job output prefix for each line in the ' |
| 84 | 'output, if --verbose is enabled. Useful for the' |
| 85 | ' pipelines that use multiple different runs of ' |
| 86 | 'jobs. The string is a template for python ' |
| 87 | 'format() function where the following arguments' |
| 88 | ' are allowed: job_name, build_number. ' |
| 89 | 'Example: --job-output-prefix=\"[ {job_name} ' |
| 90 | '#{build_number}, core ]\"'), |
| 91 | default='', |
| 92 | type=str) |
| 93 | parser.add_argument('--verbose', |
| 94 | action='store_const', |
| 95 | const=True, |
| 96 | help='Show build console output', |
| 97 | default=False) |
| 98 | return parser |
| 99 | |
| 100 | |
| 101 | def print_build_header(build, job_params, opts): |
| 102 | print('\n#############################################################') |
| 103 | print('##### Building job [{0}] #{1} (timeout={2}) with the following ' |
| 104 | 'parameters:'.format(build[0], build[1], opts.build_timeout)) |
| 105 | print('##### ' + '\n##### '.join( |
| 106 | [str(key) + ": " + str(val) for key, val in job_params.iteritems()] |
| 107 | )) |
| 108 | print('#############################################################') |
| 109 | |
| 110 | |
| 111 | def print_build_footer(build, result, url): |
| 112 | print('\n\n#############################################################') |
| 113 | print('##### Completed job [{0}] #{1} at {2}: {3}' |
| 114 | .format(build[0], build[1], url, result)) |
| 115 | print('#############################################################\n') |
| 116 | |
| 117 | |
| 118 | def run_job(opts): |
| 119 | |
| 120 | jenkins = JenkinsClient( |
| 121 | host=opts.host, |
| 122 | username=opts.username, |
| 123 | password=opts.password) |
| 124 | |
| 125 | job_params = jenkins.make_defults_params(opts.job_name) |
| 126 | job_params.update(opts.job_parameters) |
| 127 | |
| 128 | build = jenkins.run_build(opts.job_name, |
| 129 | job_params, |
| 130 | verbose=opts.verbose, |
| 131 | timeout=opts.start_timeout) |
| 132 | if opts.verbose: |
| 133 | print_build_header(build, job_params, opts) |
| 134 | |
Dennis Dmitriev | 13e804b | 2018-10-09 19:25:14 +0300 | [diff] [blame] | 135 | try: |
| 136 | jenkins.wait_end_of_build( |
| 137 | name=build[0], |
| 138 | build_id=build[1], |
| 139 | timeout=opts.build_timeout, |
| 140 | interval=1, |
| 141 | verbose=opts.verbose, |
| 142 | job_output_prefix=opts.job_output_prefix) |
| 143 | except error.TimeoutError as e: |
| 144 | print(str(e)) |
| 145 | raise |
| 146 | |
Dennis Dmitriev | 3ec2e53 | 2018-06-08 04:33:34 +0300 | [diff] [blame] | 147 | result = jenkins.build_info(name=build[0], |
| 148 | build_id=build[1])['result'] |
| 149 | if opts.verbose: |
| 150 | print_build_footer(build, result, opts.host) |
| 151 | |
| 152 | return EXIT_CODES.get(result, 2) |
| 153 | |
| 154 | |
| 155 | def main(args=None): |
| 156 | parser = load_params() |
| 157 | opts = parser.parse_args() |
| 158 | |
| 159 | if opts.host is None or opts.job_name is None: |
| 160 | print("JENKINS_URL and a job name are required!") |
| 161 | parser.print_help() |
| 162 | return 10 |
| 163 | else: |
| 164 | exit_code = run_job(opts) |
| 165 | return exit_code |
| 166 | |
| 167 | |
| 168 | if __name__ == "__main__": |
| 169 | sys.exit(main()) |