Hello community, here is the log from the commit of package python-jenkinsapi for openSUSE:Leap:15.2 checked in at 2020-04-20 12:55:17 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Leap:15.2/python-jenkinsapi (Old) and /work/SRC/openSUSE:Leap:15.2/.python-jenkinsapi.new.2738 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-jenkinsapi" Mon Apr 20 12:55:17 2020 rev:12 rq:795462 version:0.3.11 Changes: -------- --- /work/SRC/openSUSE:Leap:15.2/python-jenkinsapi/python-jenkinsapi.changes 2020-03-09 18:06:45.848859173 +0100 +++ /work/SRC/openSUSE:Leap:15.2/.python-jenkinsapi.new.2738/python-jenkinsapi.changes 2020-04-20 12:55:38.152742341 +0200 @@ -1,0 +2,35 @@ +Fri Apr 17 06:51:42 UTC 2020 - [email protected] + +- use pytest -k instead of removing tests + +------------------------------------------------------------------- +Thu Apr 16 12:13:33 UTC 2020 - [email protected] + +- version update to 0.3.11 + 0.3.11 + ------ + * jobs.py: don't yield jobs twice (#740) + * Add debug log message in JenkinsBase.process\_job\_folder to follow execution progress (#738) + * don't faile when node\_descriprion is missing (#737) + * Fix safe exit test (#736) + * don't stale feature requests or "help wanted" + * change label for stale issues + * Fixed idle state not being refreshed (#575) + * Simple plugins (#735) + 0.3.10 + ------ + * Add method for streaming build logs (#722) + * add instructions for Stale bot + * Fixed problems with changeSets (#717) + * Create Node by providing predetermined configuration (#730) + * Fix plugin versions (#734) + * Updates Requester to use a Session, maintaining cookies (#727) + * Feature/multibranch pipeline job (#715) + * Fix failures caused by plugins (#718) + * Fix for build.get\_artifacts() (#712) + * Add safe exit (#700) + * simplify crumb usage (#704) + * generate new api token for logged in user (#706) + * ignore dot files (#705) + +------------------------------------------------------------------- Old: ---- jenkinsapi-0.3.9.tar.gz New: ---- jenkinsapi-0.3.11.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-jenkinsapi.spec ++++++ --- /var/tmp/diff_new_pack.wUT7au/_old 2020-04-20 12:55:38.440742793 +0200 +++ /var/tmp/diff_new_pack.wUT7au/_new 2020-04-20 12:55:38.444742799 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-jenkinsapi # -# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2020 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -18,13 +18,13 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-jenkinsapi -Version: 0.3.9 +Version: 0.3.11 Release: 0 Summary: A Python API for accessing resources on a Jenkins continuous integration server License: MIT Group: Development/Languages/Python -Url: https://github.com/salimfadhley/jenkinsapi -Source: https://pypi.io/packages/source/j/jenkinsapi/jenkinsapi-%{version}.tar.gz +URL: https://github.com/salimfadhley/jenkinsapi +Source: https://files.pythonhosted.org/packages/source/j/jenkinsapi/jenkinsapi-%{version}.tar.gz BuildRequires: %{python_module pbr} BuildRequires: %{python_module pytz} BuildRequires: %{python_module requests} @@ -33,7 +33,15 @@ BuildRequires: python-rpm-macros Requires: python-pytz >= 2014.4 Requires: python-requests >= 2.3.0 +Requires: python-six >= 1.10.0 BuildArch: noarch +# SECTION test requirements +BuildRequires: %{python_module astroid >= 1.4.8} +BuildRequires: %{python_module mock} +BuildRequires: %{python_module pytest-mock} +BuildRequires: %{python_module pytest} +BuildRequires: %{python_module requests-kerberos} +# /SECTION %python_subpackages %description @@ -78,8 +86,14 @@ %postun %python_uninstall_alternative jenkins_invoke +%check +# jenkinsapi_tests/unittests/test_view.py +# E fixture '_view_poll' not found +# jddenkinsapi_tests/unittests/test_plugins.py +# E ConnectionError: HTTPSConnectionPool(host='updates.jenkins.io... +%pytest jenkinsapi_tests/unittests jenkinsapi_tests/test_utils -k 'not (test_view or test_plugins)' + %files %{python_files} -%defattr(-,root,root,-) %doc README.rst %license license.txt %python_alternative %{_bindir}/jenkins_invoke ++++++ jenkinsapi-0.3.9.tar.gz -> jenkinsapi-0.3.11.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/.github/stale.yml new/jenkinsapi-0.3.11/.github/stale.yml --- old/jenkinsapi-0.3.9/.github/stale.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/jenkinsapi-0.3.11/.github/stale.yml 2019-10-21 13:43:49.000000000 +0200 @@ -0,0 +1,64 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 60 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 7 + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: [] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - pinned + - security + - "[Status] Maybe Later" + - "feature request" + - "help wanted" + - "improvement request" + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: stale + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. It will be closed if no further activity occurs. Thank you + for your contributions. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +closeComment: > + Closed due to inactivity + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +# only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +# pulls: +# daysUntilStale: 30 +# markComment: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. + +# issues: +# exemptLabels: +# - confirmed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/AUTHORS new/jenkinsapi-0.3.11/AUTHORS --- old/jenkinsapi-0.3.9/AUTHORS 2019-04-13 02:46:41.000000000 +0200 +++ new/jenkinsapi-0.3.11/AUTHORS 2019-10-31 13:00:21.000000000 +0100 @@ -16,6 +16,7 @@ Bradley Harris <[email protected]> Brian Weber <[email protected]> Chris Gavin <[email protected]> +Chris Maes <[email protected]> Christophe Bliard <[email protected]> Christophe de Vienne <[email protected]> Cleber J Santos <[email protected]> @@ -42,6 +43,7 @@ Jason Swager <[email protected]> Jeremy Kao <[email protected]> Jingjing Duan <[email protected]> +Jiri Polansky <[email protected]> Joao Vale <[email protected]> John Zeller <[email protected]> Jon Passki <[email protected]> @@ -61,6 +63,7 @@ Lars Hupfeldt <[email protected]> Lars Wirzenius <[email protected]> Leshist <[email protected]> +Lucas Cimon <[email protected]> Marius van den Beek <[email protected]> Matt Hootman <[email protected]> Mickaël Schoentgen <[email protected]> @@ -72,6 +75,7 @@ Pablo Fabregat <[email protected]> Paul Belanger <[email protected]> Petr Kocandrle <[email protected]> +Przemysław Pietras <[email protected]> Rafael Eyng <[email protected]> Ramon van Alteren <[email protected]> Robert Collins <[email protected]> @@ -90,6 +94,7 @@ Sorin Sbarnea <[email protected]> Sorin Sbarnea <[email protected]> Stephen Chu <[email protected]> +Steve Bussetti <[email protected]> Steve Lounsbury <[email protected]> Sudharshan S <[email protected]> Sudharshan S <[email protected]> @@ -110,6 +115,7 @@ andrewgy8 <[email protected]> bbgobie <[email protected]> benjaminkemper <[email protected]> +chrismaes87 <[email protected]> ddavydov <[email protected]> elhostis <[email protected]> hokadiri <[email protected]> @@ -119,11 +125,14 @@ jpiron <[email protected]> jwhitworth <[email protected]> kavehv <[email protected]> +kennedy <[email protected]> kworr <[email protected]> lechat <[email protected]> lechat <[email protected]> +leon-gh <[email protected]> lphoward <[email protected]> luciali <[email protected]> +maxime-viargues-serato <[email protected]> mdear <[email protected]> mthak <[email protected]> mthakkar <[email protected]> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/ChangeLog new/jenkinsapi-0.3.11/ChangeLog --- old/jenkinsapi-0.3.9/ChangeLog 2019-04-13 02:46:41.000000000 +0200 +++ new/jenkinsapi-0.3.11/ChangeLog 2019-10-31 13:00:21.000000000 +0100 @@ -1,6 +1,35 @@ CHANGES ======= +0.3.11 +------ + +* jobs.py: don't yield jobs twice (#740) +* Add debug log message in JenkinsBase.process\_job\_folder to follow execution progress (#738) +* don't faile when node\_descriprion is missing (#737) +* Fix safe exit test (#736) +* don't stale feature requests or "help wanted" +* change label for stale issues +* Fixed idle state not being refreshed (#575) +* Simple plugins (#735) + +0.3.10 +------ + +* Add method for streaming build logs (#722) +* add instructions for Stale bot +* Fixed problems with changeSets (#717) +* Create Node by providing predetermined configuration (#730) +* Fix plugin versions (#734) +* Updates Requester to use a Session, maintaining cookies (#727) +* Feature/multibranch pipeline job (#715) +* Fix failures caused by plugins (#718) +* Fix for build.get\_artifacts() (#712) +* Add safe exit (#700) +* simplify crumb usage (#704) +* generate new api token for logged in user (#706) +* ignore dot files (#705) + 0.3.9 ----- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/PKG-INFO new/jenkinsapi-0.3.11/PKG-INFO --- old/jenkinsapi-0.3.9/PKG-INFO 2019-04-13 02:46:42.000000000 +0200 +++ new/jenkinsapi-0.3.11/PKG-INFO 2019-10-31 13:00:22.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: jenkinsapi -Version: 0.3.9 +Version: 0.3.11 Summary: A Python API for accessing resources on a Jenkins continuous-integration server. Home-page: UNKNOWN Author: Salim Fadhley, Aleksey Maksimov diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/examples/how_to/use_crumbs.py new/jenkinsapi-0.3.11/examples/how_to/use_crumbs.py --- old/jenkinsapi-0.3.9/examples/how_to/use_crumbs.py 2018-11-13 01:11:17.000000000 +0100 +++ new/jenkinsapi-0.3.11/examples/how_to/use_crumbs.py 2019-10-18 14:46:09.000000000 +0200 @@ -1,17 +1,9 @@ """ -Example of using CrumbRequester - when CSRF protection is enable in Jenkins +Example of using CrumbRequester - when CSRF protection is enabled in Jenkins """ from jenkinsapi.jenkins import Jenkins -from jenkinsapi.utils.crumb_requester import CrumbRequester -jenkins = Jenkins( - 'http://localhost:8080', username='admin', password='password', - requester=CrumbRequester( - baseurl='http://localhost:8080', - username='admin', - password='password' - ) -) +jenkins = Jenkins('http://localhost:8080', username='admin', password='password', useCrumb=True) for job_name in jenkins.jobs: - print job_name + print(job_name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi/build.py new/jenkinsapi-0.3.11/jenkinsapi/build.py --- old/jenkinsapi-0.3.9/jenkinsapi/build.py 2018-11-13 01:11:17.000000000 +0100 +++ new/jenkinsapi-0.3.11/jenkinsapi/build.py 2019-10-18 16:28:28.000000000 +0200 @@ -82,16 +82,13 @@ return self._data["builtOn"] def get_revision(self): - vcs = self._data['changeSet']['kind'] or 'git' - return getattr(self, '_get_%s_rev' % vcs, lambda: None)() + return getattr(self, '_get_%s_rev' % self._get_vcs(), lambda: None)() def get_revision_branch(self): - vcs = self._data['changeSet']['kind'] or 'git' - return getattr(self, '_get_%s_rev_branch' % vcs, lambda: None)() + return getattr(self, '_get_%s_rev_branch' % self._get_vcs(), lambda: None)() def get_repo_url(self): - vcs = self._data['changeSet']['kind'] or 'git' - return getattr(self, '_get_%s_repo_url' % vcs, lambda: None)() + return getattr(self, '_get_%s_repo_url' % self._get_vcs(), lambda: None)() def get_params(self): """ @@ -147,6 +144,18 @@ return self._data['changeSets']['items'] return [] + def _get_vcs(self): + """ + Returns a string VCS. + By default, 'git' will be used. + """ + vcs = 'git' + if 'changeSet' in self._data and 'kind' in self._data['changeSet']: + vcs = self._data['changeSet']['kind'] or 'git' + elif 'changeSets' in self._data and 'kind' in self._data['changeSets']: + vcs = self._data['changeSets']['kind'] or 'git' + return vcs + def _get_svn_rev(self): warnings.warn( "This untested function may soon be removed from Jenkinsapi " @@ -222,7 +231,7 @@ def get_artifact_dict(self): return dict( - (af.filename, af) for af in self.get_artifacts() + (af.relative_path, af) for af in self.get_artifacts() ) def get_upstream_job_name(self): @@ -494,6 +503,27 @@ else: raise JenkinsAPIException('Unknown content type for console') + def stream_logs(self, interval=0): + """ + Return generator which streams parts of text console. + """ + url = "%s/logText/progressiveText" % self.baseurl + size = 0 + more_data = True + while more_data: + resp = self.job.jenkins.requester.get_url(url, params={'start': size}) + content = resp.content + if content: + if isinstance(content, str): + yield content + elif isinstance(content, bytes): + yield content.decode('ISO-8859-1') + else: + raise JenkinsAPIException('Unknown content type for console') + size = resp.headers['X-Text-Size'] + more_data = resp.headers.get('X-More-Data') + sleep(interval) + def get_estimated_duration(self): """ Return the estimated build duration (in seconds) or none. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi/jenkins.py new/jenkinsapi-0.3.11/jenkinsapi/jenkins.py --- old/jenkinsapi-0.3.9/jenkinsapi/jenkins.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi/jenkins.py 2019-10-21 15:49:10.000000000 +0200 @@ -22,12 +22,14 @@ from jenkinsapi.nodes import Nodes from jenkinsapi.plugins import Plugins from jenkinsapi.plugin import Plugin +from jenkinsapi.utils.requester import Requester from jenkinsapi.views import Views from jenkinsapi.queue import Queue from jenkinsapi.fingerprint import Fingerprint from jenkinsapi.jenkinsbase import JenkinsBase -from jenkinsapi.utils.requester import Requester from jenkinsapi.custom_exceptions import JenkinsAPIException +from jenkinsapi.utils.crumb_requester import CrumbRequester + log = logging.getLogger(__name__) @@ -42,7 +44,8 @@ self, baseurl, username=None, password=None, requester=None, lazy=False, - ssl_verify=True, cert=None, timeout=10): + ssl_verify=True, cert=None, + timeout=10, useCrumb=False): """ :param baseurl: baseurl for jenkins instance including port, str :param username: username for jenkins auth, str @@ -51,14 +54,23 @@ """ self.username = username self.password = password - self.requester = requester or Requester( - username, - password, - baseurl=baseurl, - ssl_verify=ssl_verify, - cert=cert, - timeout=timeout - ) + if requester is None: + if useCrumb: + requester = CrumbRequester + else: + requester = Requester + + self.requester = requester( + username, + password, + baseurl=baseurl, + ssl_verify=ssl_verify, + cert=cert, + timeout=timeout + ) + else: + self.requester = requester + self.requester.timeout = timeout self.lazy = lazy self.jobs_container = None @@ -172,6 +184,12 @@ """ return self.jobs.create(jobname, xml) + def create_multibranch_pipeline_job(self, jobname, xml, block=True, delay=60): + """ + :return: list of new Job objects + """ + return self.jobs.create_multibranch_pipeline(jobname, xml, block, delay) + def copy_job(self, jobname, newjobname): return self.jobs.copy(jobname, newjobname) @@ -335,6 +353,17 @@ } return self.nodes.create_node(name, node_dict) + def create_node_with_config(self, name, config): + """ + Create a new slave node with specific configuration. + Config should be resemble the output of node.get_node_attributes() + :param str name: name of slave + :param dict config: Node attributes for Jenkins API request to create node + (See function output Node.get_node_attributes()) + :return: node obj + """ + return self.nodes.create_node_with_config(name=name, config=config) + def get_plugins_url(self, depth): # This only ever needs to work on the base object return '%s/pluginManager/api/python?depth=%i' % (self.baseurl, depth) @@ -477,6 +506,68 @@ # so Jenkins is likely available time.sleep(1) + def safe_exit(self, wait_for_exit=True, max_wait=360): + """ restarts jenkins when no jobs are running, except for pipeline jobs """ + # NB: unlike other methods, the value of resp.status_code + # here can be 503 even when everything is normal + url = '%s/safeExit' % (self.baseurl,) + valid = self.requester.VALID_STATUS_CODES + [503, 500] + resp = self.requester.post_and_confirm_status(url, data='', + valid=valid) + if wait_for_exit: + self._wait_for_exit(max_wait=max_wait) + return resp + + def _wait_for_exit(self, max_wait=360): + # We need to make sure all non pipeline jobs have finished, + # and that jenkins is unavailable + self.__jenkins_is_unresponsive(max_wait=max_wait) + + def __jenkins_is_unresponsive(self, max_wait=360): + # Blocks until jenkins returns ConnectionError or JenkinsAPIException + # Default wait is one hour + is_alive = True + wait = 0 + while is_alive and wait < max_wait: + try: + self.requester.get_and_confirm_status( + self.baseurl, valid=[200]) + time.sleep(1) + wait += 1 + is_alive = True + except (ConnectionError, JenkinsAPIException): + # Jenkins is finally down + is_alive = False + return True + except HTTPError: + # This is a return code that is not 503, + # so Jenkins is likely available, and we need to wait + time.sleep(1) + wait += 1 + is_alive = True + + def quiet_down(self): + """ Put Jenkins in a Quiet mode, preparation for restart. no new builds started""" + # https://support.cloudbees.com/hc/en-us/articles/216118748-How-to-Start-Stop-or-Restart-your-Instance- + # NB: unlike other methods, the value of resp.status_code + # here can be 503 even when everything is normal + url = '%s/quietDown' % (self.baseurl,) + valid = self.requester.VALID_STATUS_CODES + [503, 500] + resp = self.requester.post_and_confirm_status(url, data='', + valid=valid) + return resp + + def cancel_quiet_down(self): + """ Cancel the effect of the quiet-down command """ + # https://support.cloudbees.com/hc/en-us/articles/216118748-How-to-Start-Stop-or-Restart-your-Instance- + # NB: unlike other methods, the value of resp.status_code + # here can be 503 even when everything is normal + url = '%s/cancelQuietDown' % (self.baseurl,) + valid = self.requester.VALID_STATUS_CODES + [503, 500] + resp = self.requester.post_and_confirm_status(url, data='', + valid=valid) + return resp + @property def plugins(self): return self.get_plugins() @@ -528,10 +619,24 @@ def credentials_by_id(self): return self.get_credentials(CredentialsById) + @property + def is_quieting_down(self): + url = '%s/api/python?tree=quietingDown' % (self.baseurl,) + data = self.get_data(url=url) + return data.get('quietingDown', False) + def shutdown(self): url = "%s/exit" % self.baseurl self.requester.post_and_confirm_status(url, data='') + def generate_new_api_token(self, new_token_name='Token By jenkinsapi python'): + subUrl = '/me/descriptorByName/jenkins.security.ApiTokenProperty/generateNewToken' + url = '%s%s' % (self.baseurl, subUrl) + data = urlencode({'newTokenName': new_token_name}) + response = self.requester.post_and_confirm_status(url, data=data) + token = response.json()['data']['tokenValue'] + return token + def run_groovy_script(self, script): """ Runs the requested groovy script on the Jenkins server returning the diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi/jenkinsbase.py new/jenkinsapi-0.3.11/jenkinsapi/jenkinsbase.py --- old/jenkinsapi-0.3.9/jenkinsapi/jenkinsbase.py 2019-04-12 16:44:27.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi/jenkinsbase.py 2019-10-31 12:58:24.000000000 +0100 @@ -9,6 +9,8 @@ from jenkinsapi import config from jenkinsapi.custom_exceptions import JenkinsAPIException +logger = logging.getLogger(__name__) + class JenkinsBase(object): @@ -77,13 +79,13 @@ response = requester.get_url(url, params) if response.status_code != 200: - logging.error('Failed request at %s with params: %s %s', - url, params, tree if tree else '') + logger.error('Failed request at %s with params: %s %s', + url, params, tree if tree else '') response.raise_for_status() try: return ast.literal_eval(response.text) except Exception: - logging.exception('Inappropriate content found at %s', url) + logger.exception('Inappropriate content found at %s', url) raise JenkinsAPIException('Cannot parse %s' % response.content) def pprint(self): @@ -101,6 +103,7 @@ return jobs def process_job_folder(self, folder, folder_path): + logger.debug('Processing folder %s in %s', folder['name'], folder_path) folder_path += '/job/%s' % urlquote(folder['name']) data = self.get_data(self.python_api_url(folder_path), tree='jobs[name,color]') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi/jobs.py new/jenkinsapi-0.3.11/jenkinsapi/jobs.py --- old/jenkinsapi-0.3.9/jenkinsapi/jobs.py 2018-11-25 03:54:32.000000000 +0100 +++ new/jenkinsapi-0.3.11/jenkinsapi/jobs.py 2019-10-31 12:58:24.000000000 +0100 @@ -3,6 +3,7 @@ interface for all of the jobs defined on a single Jenkins server. """ import logging +import time from jenkinsapi.job import Job from jenkinsapi.custom_exceptions import JenkinsAPIException, UnknownJob @@ -92,9 +93,10 @@ Iterate over the names & objects for all jobs """ for job in self.itervalues(): - yield job.name, job if job.name != job.get_full_name(): yield job.get_full_name(), job + else: + yield job.name, job def __contains__(self, job_name): """ @@ -109,12 +111,13 @@ if not self._data: self._data = self.poll().get('jobs', []) for row in self._data: - yield row['name'] if row['name'] != \ Job.get_full_name_from_url_and_baseurl(row['url'], self.jenkins.baseurl): yield Job.get_full_name_from_url_and_baseurl( row['url'], self.jenkins.baseurl) + else: + yield row['name'] def itervalues(self): """ @@ -162,6 +165,60 @@ return self[job_name] + def create_multibranch_pipeline(self, job_name, config, block=True, delay=60): + """ + Create a multibranch pipeline job + + :param str jobname: Name of new job + :param str config: XML configuration of new job + :param block: block until scan is finished? + :param delay: max delay to wait for scan to finish (seconds) + :returns list of new Jobs after scan + """ + if not config: + raise JenkinsAPIException('Job XML config cannot be empty') + + params = {'name': job_name} + try: + if isinstance(config, unicode): # pylint: disable=undefined-variable + config = str(config) + except NameError: + # Python2 already a str + pass + self.jenkins.requester.post_xml_and_confirm_status( + self.jenkins.get_create_url(), + data=config, + params=params + ) + # Reset to get it refreshed from Jenkins + self._data = [] + + # Launch a first scan / indexing to discover the branches... + self.jenkins.requester.post_and_confirm_status( + '{}/job/{}/build'.format(self.jenkins.baseurl, job_name), + data='', + valid=[200, 302], # expect 302 without redirects + allow_redirects=False) + + start_time = time.time() + # redirect-url does not work with indexing; + # so the only workaround found is to parse the console output untill scan has finished. + scan_finished = False + while not scan_finished and block and time.time() < start_time + delay: + indexing_console_text = self.jenkins.requester.get_url( + '{}/job/{}/indexing/consoleText'.format(self.jenkins.baseurl, job_name)) + if indexing_console_text.text.strip().split('\n')[-1].startswith('Finished:'): + scan_finished = True + time.sleep(1) + + # now search for all jobs created; those who start with job_name + '/' + jobs = [] + for name in self.jenkins.get_jobs_list(): + if name.startswith(job_name + '/'): + jobs.append(self[name]) + + return jobs + def copy(self, job_name, new_job_name): """ Copy a job diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi/node.py new/jenkinsapi-0.3.11/jenkinsapi/node.py --- old/jenkinsapi-0.3.9/jenkinsapi/node.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi/node.py 2019-10-31 12:58:24.000000000 +0100 @@ -6,8 +6,9 @@ import xml.etree.ElementTree as ET +import time from jenkinsapi.jenkinsbase import JenkinsBase -from jenkinsapi.custom_exceptions import PostRequired +from jenkinsapi.custom_exceptions import PostRequired, TimeOut from jenkinsapi.custom_exceptions import JenkinsAPIException from six.moves.urllib.parse import quote as urlquote @@ -171,7 +172,7 @@ 'type': 'hudson.slaves.DumbSlave$DescriptorImpl', 'json': json.dumps({ 'name': self.name, - 'nodeDescription': na['node_description'], + 'nodeDescription': na.get('node_description', ''), 'numExecutors': na['num_executors'], 'remoteFS': na['remote_fs'], 'labelString': na['labels'], @@ -201,7 +202,7 @@ return self._data['jnlpAgent'] def is_idle(self): - return self._data['idle'] + return self.poll(tree='idle')['idle'] def set_online(self): """ @@ -469,6 +470,27 @@ # no need to poll as the architecture will never change return self.get_monitor('ArchitectureMonitor', poll_monitor=False) + def block_until_idle(self, timeout, poll_time=5): + """ + Blocks until the node become idle. + :param timeout: Time in second when the wait is aborted. + :param poll_time: Interval in seconds between each check. + :@raise TimeOut + """ + start_time = time.time() + while not self.is_idle() and (time.time() - start_time) < timeout: + log.debug( + "Waiting for the node to become idle. Elapsed time: %s", + (time.time() - start_time) + ) + time.sleep(poll_time) + + if not self.is_idle(): + raise TimeOut( + "The node has not become idle after {} minutes." + .format(timeout/60) + ) + def get_response_time(self): """ Returns the node's average response time. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi/nodes.py new/jenkinsapi-0.3.11/jenkinsapi/nodes.py --- old/jenkinsapi-0.3.9/jenkinsapi/nodes.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi/nodes.py 2019-10-18 14:46:09.000000000 +0200 @@ -154,3 +154,25 @@ self.jenkins.requester.post_and_confirm_status(url, data=data) self.poll() return self[name] + + def create_node_with_config(self, name, config): + """ + Create a new slave node with specific configuration. + Config should be resemble the output of node.get_node_attributes() + :param str name: name of slave + :param dict config: Node attributes for Jenkins API request to create node + (See function output Node.get_node_attributes()) + :return: node obj + """ + if name in self: + return self[name] + + if not isinstance(config, dict): + return None + url = ('%s/computer/doCreateItem?%s' + % (self.jenkins.baseurl, + urlencode(config))) + data = {'json': urlencode(config)} + self.jenkins.requester.post_and_confirm_status(url, data=data) + self.poll() + return self[name] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi/plugins.py new/jenkinsapi-0.3.11/jenkinsapi/plugins.py --- old/jenkinsapi-0.3.9/jenkinsapi/plugins.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi/plugins.py 2019-10-21 15:49:10.000000000 +0200 @@ -50,7 +50,7 @@ @property def update_center_dict(self): - update_center = 'https://updates.jenkins-ci.org/update-center.json' + update_center = 'https://updates.jenkins.io/update-center.json' jsonp = requests.get(update_center).content.decode('utf-8') return json.loads(jsonp_to_json(jsonp)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi/utils/requester.py new/jenkinsapi-0.3.11/jenkinsapi/utils/requester.py --- old/jenkinsapi-0.3.9/jenkinsapi/utils/requester.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi/utils/requester.py 2019-10-18 14:46:09.000000000 +0200 @@ -77,6 +77,7 @@ self.ssl_verify = kwargs.get('ssl_verify', ssl_verify) self.cert = kwargs.get('cert', cert) self.timeout = kwargs.get('timeout', timeout) + self.session = requests.Session() def get_request_dict( self, params=None, data=None, files=None, headers=None, **kwargs): @@ -140,7 +141,7 @@ allow_redirects=allow_redirects, stream=stream ) - return requests.get(self._update_url_scheme(url), **requestKwargs) + return self.session.get(self._update_url_scheme(url), **requestKwargs) def post_url(self, url, params=None, data=None, files=None, headers=None, allow_redirects=True, **kwargs): @@ -151,7 +152,7 @@ headers=headers, allow_redirects=allow_redirects, **kwargs) - return requests.post(self._update_url_scheme(url), **requestKwargs) + return self.session.post(self._update_url_scheme(url), **requestKwargs) def post_xml_and_confirm_status( self, url, params=None, data=None, valid=None): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi.egg-info/PKG-INFO new/jenkinsapi-0.3.11/jenkinsapi.egg-info/PKG-INFO --- old/jenkinsapi-0.3.9/jenkinsapi.egg-info/PKG-INFO 2019-04-13 02:46:41.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi.egg-info/PKG-INFO 2019-10-31 13:00:21.000000000 +0100 @@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: jenkinsapi -Version: 0.3.9 +Version: 0.3.11 Summary: A Python API for accessing resources on a Jenkins continuous-integration server. Home-page: UNKNOWN Author: Salim Fadhley, Aleksey Maksimov diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi.egg-info/SOURCES.txt new/jenkinsapi-0.3.11/jenkinsapi.egg-info/SOURCES.txt --- old/jenkinsapi-0.3.9/jenkinsapi.egg-info/SOURCES.txt 2019-04-13 02:46:41.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi.egg-info/SOURCES.txt 2019-10-31 13:00:21.000000000 +0100 @@ -12,6 +12,7 @@ test-requirements.txt tox.ini .github/ISSUE_TEMPLATE.md +.github/stale.yml bin/pipelint doc/.gitignore doc/build.properties @@ -110,6 +111,7 @@ jenkinsapi_tests/systests/test_downstream_upstream.py jenkinsapi_tests/systests/test_env_vars.py jenkinsapi_tests/systests/test_executors.py +jenkinsapi_tests/systests/test_generate_new_api_token.py jenkinsapi_tests/systests/test_invocation.py jenkinsapi_tests/systests/test_jenkins.py jenkinsapi_tests/systests/test_jenkins_artifacts.py @@ -118,7 +120,9 @@ jenkinsapi_tests/systests/test_parameterized_builds.py jenkinsapi_tests/systests/test_plugins.py jenkinsapi_tests/systests/test_queue.py +jenkinsapi_tests/systests/test_quiet_down.py jenkinsapi_tests/systests/test_restart.py +jenkinsapi_tests/systests/test_safe_exit.py jenkinsapi_tests/systests/test_scm.py jenkinsapi_tests/systests/test_views.py jenkinsapi_tests/systests/view_configs.py diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi.egg-info/pbr.json new/jenkinsapi-0.3.11/jenkinsapi.egg-info/pbr.json --- old/jenkinsapi-0.3.9/jenkinsapi.egg-info/pbr.json 2019-04-13 02:46:41.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi.egg-info/pbr.json 2019-10-31 13:00:21.000000000 +0100 @@ -1 +1 @@ -{"git_version": "b4d62f1", "is_release": true} \ No newline at end of file +{"git_version": "dce4073", "is_release": true} \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/conftest.py new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/conftest.py --- old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/conftest.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/conftest.py 2019-10-21 13:39:57.000000000 +0200 @@ -13,30 +13,32 @@ # Extra plugins required by the systests PLUGIN_DEPENDENCIES = [ - "http://updates.jenkins-ci.org/latest/" + "http://updates.jenkins.io/latest/" "apache-httpcomponents-client-4-api.hpi", - "http://updates.jenkins-ci.org/latest/jsch.hpi", - "http://updates.jenkins-ci.org/latest/trilead-api.hpi", - "http://updates.jenkins-ci.org/latest/workflow-api.hpi", - "http://updates.jenkins-ci.org/latest/display-url-api.hpi", - "http://updates.jenkins-ci.org/latest/workflow-step-api.hpi", - "http://updates.jenkins-ci.org/latest/workflow-scm-step.hpi", - "http://updates.jenkins-ci.org/latest/icon-shim.hpi", - "http://updates.jenkins-ci.org/latest/junit.hpi", - "http://updates.jenkins-ci.org/latest/script-security.hpi", - "http://updates.jenkins-ci.org/latest/matrix-project.hpi", - "http://updates.jenkins-ci.org/latest/credentials.hpi", - "http://updates.jenkins-ci.org/latest/ssh-credentials.hpi", - "http://updates.jenkins-ci.org/latest/scm-api.hpi", - "http://updates.jenkins-ci.org/latest/mailer.hpi", - "http://updates.jenkins-ci.org/latest/git.hpi", - "http://updates.jenkins-ci.org/latest/git-client.hpi", - "https://updates.jenkins-ci.org/latest/nested-view.hpi", - "https://updates.jenkins-ci.org/latest/ssh-slaves.hpi", - "https://updates.jenkins-ci.org/latest/structs.hpi", - "http://updates.jenkins-ci.org/latest/plain-credentials.hpi", - "http://updates.jenkins-ci.org/latest/envinject.hpi", - "http://updates.jenkins-ci.org/latest/envinject-api.hpi" + "http://updates.jenkins.io/latest/jsch.hpi", + "http://updates.jenkins.io/download/plugins/trilead-api/1.0.5/" + "trilead-api.hpi", + "http://updates.jenkins.io/latest/workflow-api.hpi", + "http://updates.jenkins.io/latest/display-url-api.hpi", + "http://updates.jenkins.io/latest/workflow-step-api.hpi", + "http://updates.jenkins.io/latest/workflow-scm-step.hpi", + "http://updates.jenkins.io/latest/icon-shim.hpi", + "http://updates.jenkins.io/download/plugins/junit/1.28/junit.hpi", + "http://updates.jenkins.io/latest/script-security.hpi", + "http://updates.jenkins.io/latest/matrix-project.hpi", + "http://updates.jenkins.io/latest/credentials.hpi", + "http://updates.jenkins.io/latest/ssh-credentials.hpi", + "http://updates.jenkins.io/latest/scm-api.hpi", + "http://updates.jenkins.io/latest/mailer.hpi", + "http://updates.jenkins.io/latest/git.hpi", + "http://updates.jenkins.io/latest/git-client.hpi", + "https://updates.jenkins.io/latest/nested-view.hpi", + "https://updates.jenkins.io/latest/ssh-slaves.hpi", + "https://updates.jenkins.io/latest/structs.hpi", + "http://updates.jenkins.io/latest/plain-credentials.hpi", + "http://updates.jenkins.io/latest/envinject.hpi", + "http://updates.jenkins.io/latest/envinject-api.hpi", + "http://updates.jenkins.io/latest/jdk-tool.hpi" ] @@ -122,7 +124,7 @@ def jenkins(launched_jenkins): url = launched_jenkins.jenkins_url - jenkins_instance = Jenkins(url) + jenkins_instance = Jenkins(url, timeout=30) _delete_all_jobs(jenkins_instance) _delete_all_views(jenkins_instance) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_generate_new_api_token.py new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_generate_new_api_token.py --- old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_generate_new_api_token.py 1970-01-01 01:00:00.000000000 +0100 +++ new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_generate_new_api_token.py 2019-10-18 14:46:09.000000000 +0200 @@ -0,0 +1,24 @@ + +""" +System tests for generation new api token for logged in user +""" +import pytest +import logging +from jenkinsapi.utils.crumb_requester import CrumbRequester + + +log = logging.getLogger(__name__) + + [email protected]_new_api_token +def test_generate_new_api_token(jenkins_admin_admin): + jenkins_admin_admin.requester = CrumbRequester( + baseurl=jenkins_admin_admin.baseurl, + username=jenkins_admin_admin.username, + password=jenkins_admin_admin.password + ) + jenkins_admin_admin.poll() + + new_token = jenkins_admin_admin.generate_new_api_token() # generate new token + log.info('newly generated token: %s', new_token) + assert new_token is not None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_jenkins.py new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_jenkins.py --- old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_jenkins.py 2018-11-26 14:23:11.000000000 +0100 +++ new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_jenkins.py 2019-10-18 14:46:09.000000000 +0200 @@ -206,7 +206,7 @@ def test_install_delete_single_plugin_string(jenkins): - plugin_name = 'antisamy-markup-formatter' + plugin_name = 'simple-theme-plugin' plugin_version = 'latest' plugin = ('%s@%s') % (plugin_name, plugin_version) @@ -223,7 +223,7 @@ def test_install_delete_single_plugin_object(jenkins): - plugin_name = 'antisamy-markup-formatter' + plugin_name = 'simple-theme-plugin' plugin_version = 'latest' plugin = Plugin(('%s@%s') % (plugin_name, plugin_version)) @@ -240,7 +240,7 @@ def test_install_delete_multiple_plugins_mix_string_object(jenkins): - plugin_one_name = 'antisamy-markup-formatter' + plugin_one_name = 'simple-theme-plugin' plugin_one_version = 'latest' plugin_one = ('%s@%s') % (plugin_one_name, plugin_one_version) plugin_two_name = 'docker-commons' @@ -264,4 +264,4 @@ def test_run_groovy_script(jenkins): expected_result = 'Hello world!' result = jenkins.run_groovy_script('print "%s"' % expected_result) - assert result == 'Hello world!' + assert result.strip() == 'Hello world!' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_plugins.py new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_plugins.py --- old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_plugins.py 2017-12-14 14:59:25.000000000 +0100 +++ new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_plugins.py 2019-10-21 13:39:57.000000000 +0200 @@ -9,6 +9,16 @@ log = logging.getLogger(__name__) +def test_plugin_data(jenkins): + # It takes time to get plugins json from remote + timeout = jenkins.requester.timeout + jenkins.requester.timeout = 60 + jenkins.plugins.check_updates_server() + jenkins.requester.timeout = timeout + + assert 'mailer' in jenkins.plugins + + def test_get_missing_plugin(jenkins): plugins = jenkins.get_plugins() with pytest.raises(KeyError): @@ -27,39 +37,38 @@ plugins = jenkins.get_plugins(depth=2) _, plugin = next(plugins.iteritems()) + assert isinstance(plugin, Plugin) + def test_delete_inexistant_plugin(jenkins): with pytest.raises(KeyError): - plugins = jenkins.plugins - - del plugins[random_string()] + del jenkins.plugins[random_string()] def test_install_uninstall_plugin(jenkins): - plugins = jenkins.plugins - plugin_name = 'async-http-client' + plugin_name = 'suppress-stack-trace' plugin_dict = { 'shortName': plugin_name, 'version': 'latest', } - plugins[plugin_name] = Plugin(plugin_dict) + jenkins.plugins[plugin_name] = Plugin(plugin_dict) - assert plugin_name in plugins + assert plugin_name in jenkins.plugins - plugin = plugins[plugin_name] + plugin = jenkins.get_plugins()[plugin_name] assert isinstance(plugin, Plugin) assert plugin.shortName == plugin_name - del plugins[plugin_name] - assert plugins[plugin_name].deleted + del jenkins.plugins[plugin_name] + assert jenkins.plugins[plugin_name].deleted def test_install_multiple_plugins(jenkins): - plugin_one_name = 'jenkins-cloudformation-plugin' + plugin_one_name = 'keyboard-shortcuts-plugin' plugin_one_version = 'latest' plugin_one = "@".join((plugin_one_name, plugin_one_version)) - plugin_two = Plugin({'shortName': 'anything-goes-formatter', 'version': 'latest'}) + plugin_two = Plugin({'shortName': 'emotional-jenkins-plugin', 'version': 'latest'}) assert isinstance(plugin_two, Plugin) @@ -70,23 +79,28 @@ assert plugin_one_name in jenkins.plugins assert plugin_two.shortName in jenkins.plugins + del jenkins.plugins['keyboard-shortcuts-plugin'] + del jenkins.plugins['emotional-jenkins-plugin'] + def test_downgrade_plugin(jenkins): - plugin_name = 'amazon-ecs' - plugin_version = '1.5' # This is explicitly not the latest version + plugin_name = 'console-badge' + plugin_version = 'latest' plugin = Plugin({'shortName': plugin_name, 'version': plugin_version}) assert isinstance(plugin, Plugin) # Need to restart when not installing the latest version - jenkins.install_plugins([plugin], restart=True, wait_for_reboot=True) + jenkins.install_plugins([plugin]) installed_plugin = jenkins.plugins[plugin_name] - assert installed_plugin.version == '1.5' + assert installed_plugin.version == '1.1' - older_plugin = Plugin({'shortName': plugin_name, 'version': '1.4'}) + older_plugin = Plugin({'shortName': plugin_name, 'version': '1.0'}) jenkins.install_plugins([older_plugin], restart=True, wait_for_reboot=True) installed_older_plugin = jenkins.plugins[plugin_name] - assert installed_older_plugin.version == '1.4' + assert installed_older_plugin.version == '1.0' + + del jenkins.plugins[plugin_name] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_quiet_down.py new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_quiet_down.py --- old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_quiet_down.py 1970-01-01 01:00:00.000000000 +0100 +++ new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_quiet_down.py 2019-10-18 14:46:09.000000000 +0200 @@ -0,0 +1,27 @@ + +""" +System tests for setting jenkins in quietDown mode +""" +import pytest +import logging + + +log = logging.getLogger(__name__) + + [email protected]_these_please +def test_quiet_down_and_cancel_quiet_down(jenkins): + jenkins.poll() # jenkins should be alive + + jenkins.quiet_down() # put Jenkins in quietDown mode + # is_quieting_down = jenkins.is_quieting_down + assert jenkins.is_quieting_down is True + + jenkins.poll() # jenkins should be alive + + jenkins.cancel_quiet_down() # leave quietDown mode + + # is_quieting_down = jenkins_api['quietingDown'] + assert jenkins.is_quieting_down is False + + jenkins.poll() # jenkins should be alive diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_safe_exit.py new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_safe_exit.py --- old/jenkinsapi-0.3.9/jenkinsapi_tests/systests/test_safe_exit.py 1970-01-01 01:00:00.000000000 +0100 +++ new/jenkinsapi-0.3.11/jenkinsapi_tests/systests/test_safe_exit.py 2019-10-21 15:49:10.000000000 +0200 @@ -0,0 +1,44 @@ +""" +System tests for `jenkinsapi.jenkins` module. +""" +import time +import logging +import pytest +from jenkinsapi.build import Build +from jenkinsapi_tests.test_utils.random_strings import random_string +from jenkinsapi_tests.systests.job_configs import LONG_RUNNING_JOB + + +log = logging.getLogger(__name__) + + [email protected]_these_please +def test_safe_exit(jenkins): + job_name = 'Bcreate_%s' % random_string() + job = jenkins.create_job(job_name, LONG_RUNNING_JOB) + qq = job.invoke() + time.sleep(3) + bn = qq.block_until_building(delay=3).get_number() + assert isinstance(bn, int) + + build = qq.get_build() + assert isinstance(build, Build) + assert build.is_running() + + # A job is now running and safe_exit should await running jobs + # Call, but wait only for 5 seconds then cancel exit + jenkins.safe_exit(wait_for_exit=False) + time.sleep(5) + + jenkins.cancel_quiet_down() # leave quietDown mode + assert jenkins.is_quieting_down is False + + build.stop() + # if we call next line right away - Jenkins have no time to stop job + # so we wait a bit + while build.is_running(): + time.sleep(0.5) + + console = build.get_console() + assert isinstance(console, str) + assert 'Started by user' in console diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_tests/unittests/test_jenkins.py new/jenkinsapi-0.3.11/jenkinsapi_tests/unittests/test_jenkins.py --- old/jenkinsapi-0.3.9/jenkinsapi_tests/unittests/test_jenkins.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi_tests/unittests/test_jenkins.py 2019-10-18 14:46:09.000000000 +0200 @@ -1,4 +1,5 @@ import pytest +from collections import namedtuple import jenkinsapi from jenkinsapi.plugins import Plugins @@ -19,6 +20,34 @@ 'color': 'blue'}, ] } +MULTIBRANCH_JOBS_DATA = { + 'jobs': [ + {'name': 'multibranch-repo/master', + 'url': 'http://localhost:8080/job/multibranch-repo/job/master', + 'color': 'blue'}, + {'name': 'multibranch-repo/develop', + 'url': 'http://localhost:8080/job/multibranch-repo/job/develop', + 'color': 'blue'}, + ] +} +SCAN_MULTIBRANCH_PIPELINE_LOG = """ +Started by timer +[Fri Jul 05 06:46:00 CEST 2019] Starting branch indexing... +Connecting to https://stash.macq.eu using Jenkins/****** (jenkins-ldap) +Repository type: Git +Looking up internal/base for branches +Checking branch master from internal/base + 'Jenkinsfile' found + Met criteria +No changes detected: master (still at 26d4d8a673f57a957fd5a23f5adfe0be02089294) + + 1 branches were processed +Looking up internal/base for pull requests + + 0 pull requests were processed +[Fri Jul 05 06:46:01 CEST 2019] Finished branch indexing. Indexing took 1.1 sec +Finished: SUCCESS +""" @pytest.fixture(scope='function') @@ -112,6 +141,45 @@ assert 'Job XML config cannot be empty' in str(ar.value) +def test_create_multibranch_pipeline_job(mocker, monkeypatch): + def fake_jenkins_poll(cls, tree=None): # pylint: disable=unused-argument + # return multibranch jobs and other jobs. + # create_multibranch_pipeline_job is supposed to filter out the MULTIBRANCH jobs + return { + 'jobs': TWO_JOBS_DATA['jobs'] + MULTIBRANCH_JOBS_DATA['jobs'] + } + + def fake_job_poll(cls, tree=None): # pylint: disable=unused-argument + return {} + + monkeypatch.setattr(JenkinsBase, '_poll', fake_jenkins_poll) + monkeypatch.setattr(Jenkins, '_poll', fake_jenkins_poll) + monkeypatch.setattr(Job, '_poll', fake_job_poll) + + mock_requester = Requester(username='foouser', password='foopassword') + mock_requester.post_xml_and_confirm_status = mocker.MagicMock( + return_value='' + ) + mock_requester.post_and_confirm_status = mocker.MagicMock( + return_value='' + ) + get_response = namedtuple('get_response', 'text') + mock_requester.get_url = mocker.MagicMock( + return_value=get_response(text=SCAN_MULTIBRANCH_PIPELINE_LOG) + ) + jenkins = Jenkins('http://localhost:8080/', + username='foouser', password='foopassword', + requester=mock_requester) + + jobs = jenkins.create_multibranch_pipeline_job("multibranch-repo", "multibranch-xml-content") + + for idx, job_instance in enumerate(jobs): + assert job_instance.name == MULTIBRANCH_JOBS_DATA['jobs'][idx]['name'] + + # make sure we didn't get more jobs. + assert len(MULTIBRANCH_JOBS_DATA['jobs']) == len(jobs) + + def test_get_jenkins_obj_from_url(mocker, monkeypatch): def fake_jenkins_poll(cls, tree=None): # pylint: disable=unused-argument return TWO_JOBS_DATA diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_tests/unittests/test_requester.py new/jenkinsapi-0.3.11/jenkinsapi_tests/unittests/test_requester.py --- old/jenkinsapi-0.3.9/jenkinsapi_tests/unittests/test_requester.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi_tests/unittests/test_requester.py 2019-10-18 14:46:09.000000000 +0200 @@ -255,7 +255,7 @@ def fake_get(*args, **kwargs): # pylint: disable=unused-argument return 'SUCCESS' - monkeypatch.setattr(requests, 'get', fake_get) + monkeypatch.setattr(requests.Session, 'get', fake_get) req = Requester('foo', 'bar') response = req.get_url( @@ -270,7 +270,7 @@ def fake_post(*args, **kwargs): # pylint: disable=unused-argument return 'SUCCESS' - monkeypatch.setattr(requests, 'post', fake_post) + monkeypatch.setattr(requests.Session, 'post', fake_post) req = Requester('foo', 'bar') response = req.post_url( @@ -285,7 +285,7 @@ def fake_post(*args, **kwargs): # pylint: disable=unused-argument return 'SUCCESS' - monkeypatch.setattr(requests, 'post', fake_post) + monkeypatch.setattr(requests.Session, 'post', fake_post) req = Requester('foo', 'bar') with pytest.raises(AssertionError): @@ -304,7 +304,7 @@ def fake_post(*args, **kwargs): # pylint: disable=unused-argument return FakeResponse() - monkeypatch.setattr(requests, 'post', fake_post) + monkeypatch.setattr(requests.Session, 'post', fake_post) req = Requester('foo', 'bar') ret = req.post_xml_and_confirm_status( @@ -319,7 +319,7 @@ def fake_post(*args, **kwargs): # pylint: disable=unused-argument return 'SUCCESS' - monkeypatch.setattr(requests, 'post', fake_post) + monkeypatch.setattr(requests.Session, 'post', fake_post) req = Requester('foo', 'bar') with pytest.raises(AssertionError): @@ -338,7 +338,7 @@ def fake_post(*args, **kwargs): # pylint: disable=unused-argument return FakeResponse() - monkeypatch.setattr(requests, 'post', fake_post) + monkeypatch.setattr(requests.Session, 'post', fake_post) req = Requester('foo', 'bar') ret = req.post_and_confirm_status( @@ -359,7 +359,7 @@ def fake_post(*args, **kwargs): # pylint: disable=unused-argument return FakeResponse() - monkeypatch.setattr(requests, 'post', fake_post) + monkeypatch.setattr(requests.Session, 'post', fake_post) req = Requester('foo', 'bar') with pytest.raises(JenkinsAPIException): @@ -378,7 +378,7 @@ def fake_get(*args, **kwargs): # pylint: disable=unused-argument return FakeResponse() - monkeypatch.setattr(requests, 'get', fake_get) + monkeypatch.setattr(requests.Session, 'get', fake_get) req = Requester('foo', 'bar') ret = req.get_and_confirm_status( @@ -398,7 +398,7 @@ def fake_get(*args, **kwargs): # pylint: disable=unused-argument return FakeResponse() - monkeypatch.setattr(requests, 'get', fake_get) + monkeypatch.setattr(requests.Session, 'get', fake_get) req = Requester('foo', 'bar', baseurl='http://dummy') with pytest.raises(JenkinsAPIException): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/jenkinsapi_utils/jenkins_launcher.py new/jenkinsapi-0.3.11/jenkinsapi_utils/jenkins_launcher.py --- old/jenkinsapi-0.3.9/jenkinsapi_utils/jenkins_launcher.py 2019-04-12 15:45:38.000000000 +0200 +++ new/jenkinsapi-0.3.11/jenkinsapi_utils/jenkins_launcher.py 2019-10-21 15:49:10.000000000 +0200 @@ -65,10 +65,10 @@ Launch jenkins """ JENKINS_WEEKLY_WAR_URL = ( - "http://mirrors.jenkins-ci.org/war/latest/jenkins.war" + "http://updates.jenkins.io/latest/jenkins.war" ) JENKINS_LTS_WAR_URL = ( - "http://mirrors.jenkins-ci.org/war-stable/latest/jenkins.war" + "https://updates.jenkins.io/stable/latest/jenkins.war" ) def __init__(self, local_orig_dir, systests_dir, war_name, plugin_urls=None, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/pylintrc new/jenkinsapi-0.3.11/pylintrc --- old/jenkinsapi-0.3.9/pylintrc 2018-11-26 13:33:20.000000000 +0100 +++ new/jenkinsapi-0.3.11/pylintrc 2019-10-18 14:46:09.000000000 +0200 @@ -51,7 +51,7 @@ # E1103: %s %r has no %r member (but some types could not be inferred) - fails to infer real members of types, e.g. in Celery # W0231: method from base class is not called - complains about not invoking empty __init__s in parents, which is annoying # R0921: abstract class not referenced, when in fact referenced from another egg -disable=F0401,E0611,E1101,W0142,W0212,R0201,W0703,R0801,R0901,W0511,E1103,W0231,R0921,W0402,I0011,wrong-import-position,wrong-import-order,ungrouped-imports,redefined-variable-type,missing-docstring,redefined-outer-name,redefined-builtin,relative-import,c-extension-no-member,useless-object-inheritance,no-else-return,consider-using-in,consider-using-dict-comprehension,unnecessary-pass +disable=F0401,E0611,E1101,W0142,W0212,R0201,W0703,R0801,R0901,W0511,E1103,W0231,R0921,W0402,I0011,wrong-import-position,wrong-import-order,ungrouped-imports,redefined-variable-type,missing-docstring,redefined-outer-name,redefined-builtin,relative-import,c-extension-no-member,useless-object-inheritance,no-else-return,consider-using-in,consider-using-dict-comprehension,unnecessary-pass,unnecessary-comprehension [REPORTS] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/jenkinsapi-0.3.9/setup.cfg new/jenkinsapi-0.3.11/setup.cfg --- old/jenkinsapi-0.3.9/setup.cfg 2019-04-13 02:46:42.000000000 +0200 +++ new/jenkinsapi-0.3.11/setup.cfg 2019-10-31 13:00:22.000000000 +0100 @@ -51,7 +51,7 @@ universal = 1 [pycodestyle] -exclude = .tox,doc/source/conf.py,build +exclude = .tox,doc/source/conf.py,build,.venv,.eggs max-line-length = 99 [egg_info]
