diff --git a/jenkins_cli/__init__.py b/jenkins_cli/__init__.py index aed7e55..c267b41 100644 --- a/jenkins_cli/__init__.py +++ b/jenkins_cli/__init__.py @@ -17,6 +17,7 @@ def main(): parser.add_argument('--host', metavar='jenkins-url', help='Jenkins Host', default=None) parser.add_argument('--username', metavar='username', help='Jenkins Username', default=None) parser.add_argument('--password', metavar='password', help='Jenkins Password', default=None) + parser.add_argument('--ignore-ssl', action='store_true', help='Disable SSL verification') parser.add_argument('--version', '-v', action='version', version='jenkins-cli %s' % version) subparsers = parser.add_subparsers(title='Available commands', dest='jenkins_command') diff --git a/jenkins_cli/cli.py b/jenkins_cli/cli.py index 4596d0f..eb4e1cb 100644 --- a/jenkins_cli/cli.py +++ b/jenkins_cli/cli.py @@ -3,6 +3,7 @@ import sys import datetime from time import time, sleep +import ssl import jenkins import socket from xml.etree import ElementTree @@ -31,6 +32,10 @@ } +# Default constants to parse booleans from .jenkins-cli +TRUE = ['yes', 'Yes', 'YES', 'True', 'true', 'TRUE', '1'] + + ENDCOLLOR = '\033[0m' ANIME_SYMBOL = ['..', '>>'] AUTHOR_COLLOR = '\033[94m' @@ -44,6 +49,20 @@ } +def setup_ssl_context(ignore_ssl): + """Disable SSL verification if ignore_ssl=True""" + + if ignore_ssl: + try: + _create_unverified_https_context = ssl._create_unverified_context + except AttributeError: + # Legacy Python that doesn't verify HTTPS certificates by default + pass + else: + # Handle target environment that doesn't support HTTPS verification + ssl._create_default_https_context = _create_unverified_https_context + + def get_formated_status(job_color, format_pattern="%(color)s%(symbol)s%(run_status)s%(endcollor)s", extra_params=None): if not extra_params: extra_params = {} @@ -87,16 +106,18 @@ class JenkinsCli(object): "%s branch set to: %s") def __init__(self, args, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): - self.jenkins = self.auth(args.host, args.username, args.password, timeout) + self.jenkins = self.auth(args.host, args.username, args.password, args.ignore_ssl, timeout) @classmethod - def auth(cls, host=None, username=None, password=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): + def auth(cls, host=None, username=None, password=None, ignore_ssl=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): if host is None or username is None or password is None: settings_dict = cls.read_settings_from_file() try: host = host or settings_dict['host'] username = username or settings_dict.get('username', None) password = password or settings_dict.get('password', None) + cls.ignore_ssl = ignore_ssl or settings_dict.get('ignore_ssl', 'False') in TRUE + except KeyError: raise CliException('Jenkins "host" should be specified by the command-line option or in the .jenkins-cli file') return jenkins.Jenkins(host, username, password, timeout) @@ -124,6 +145,7 @@ def read_settings_from_file(cls): return settings_dict def run_command(self, args): + setup_ssl_context(self.ignore_ssl) command = args.jenkins_command getattr(self, command)(args) diff --git a/tests/test_cli.py b/tests/test_cli.py index 58b2d59..80d6f19 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -311,5 +311,6 @@ def info_side_effect(name, number): self.patched_print.assert_has_calls([mock.call("FDN Job1 estimated time left %s" % timedelta(seconds=TS))], [mock.call("FDN Job5 estimated time left %s" % timedelta(seconds=TS))]) + if __name__ == '__main__': unittest.main()