Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to the jenkins_build module and new jenkins_build_info module #7204

Merged
merged 16 commits into from
Sep 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/BOTMETA.yml
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,9 @@ files:
labels: jboss
maintainers: $team_jboss jhoekx
$modules/jenkins_build.py:
maintainers: brettmilford unnecessary-username
maintainers: brettmilford unnecessary-username juanmcasanova
$modules/jenkins_build_info.py:
maintainers: juanmcasanova
$modules/jenkins_job.py:
maintainers: sermilrod
$modules/jenkins_job_info.py:
Expand Down
4 changes: 4 additions & 0 deletions changelogs/fragments/improvements-to-jenkins-build-module.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
minor_changes:
- jenkins_build - add new ``detach`` option, which allows the module to exit successfully as long as the build is created (default functionality is still waiting for the build to end before exiting) (https://github.com/ansible-collections/community.general/pull/7204).
- jenkins_build - add new ``time_between_checks`` option, which allows to configure the wait time between requests to the Jenkins server (https://github.com/ansible-collections/community.general/pull/7204).
29 changes: 27 additions & 2 deletions plugins/modules/jenkins_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
author:
- Brett Milford (@brettmilford)
- Tong He (@unnecessary-username)
- Juan Casanova (@juanmcasanova)
extends_documentation_fragment:
- community.general.attributes
attributes:
Expand Down Expand Up @@ -65,6 +66,19 @@
description:
- User to authenticate with the Jenkins server.
type: str
detach:
description:
- Enable detached mode to not wait for the build end.
default: false
type: bool
version_added: 7.4.0
time_between_checks:
description:
- Time in seconds to wait between requests to the Jenkins server.
- This times must be higher than the configured quiet time for the job.
default: 10
type: int
version_added: 7.4.0
'''

EXAMPLES = '''
Expand Down Expand Up @@ -152,6 +166,8 @@ def __init__(self, module):
self.user = module.params.get('user')
self.jenkins_url = module.params.get('url')
self.build_number = module.params.get('build_number')
self.detach = module.params.get('detach')
self.time_between_checks = module.params.get('time_between_checks')
self.server = self.get_jenkins_connection()

self.result = {
Expand Down Expand Up @@ -235,7 +251,14 @@ def get_result(self):
build_status = self.get_build_status()

if build_status['result'] is None:
sleep(10)
# If detached mode is active mark as success, we wouldn't be able to get here if it didn't exist
if self.detach:
result['changed'] = True
result['build_info'] = build_status

return result

sleep(self.time_between_checks)
self.get_result()
else:
if self.state == "stopped" and build_status['result'] == "ABORTED":
Expand Down Expand Up @@ -273,6 +296,8 @@ def main():
token=dict(no_log=True),
url=dict(default="http://localhost:8080"),
user=dict(),
detach=dict(type='bool', default=False),
time_between_checks=dict(type='int', default=10),
),
mutually_exclusive=[['password', 'token']],
required_if=[['state', 'absent', ['build_number'], True], ['state', 'stopped', ['build_number'], True]],
Expand All @@ -288,7 +313,7 @@ def main():
else:
jenkins_build.absent_build()

sleep(10)
sleep(jenkins_build.time_between_checks)
result = jenkins_build.get_result()
module.exit_json(**result)

Expand Down
210 changes: 210 additions & 0 deletions plugins/modules/jenkins_build_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later

from __future__ import absolute_import, division, print_function
__metaclass__ = type

DOCUMENTATION = '''
---
module: jenkins_build_info
short_description: Get information about Jenkins builds
version_added: 7.4.0
description:
- Get information about Jenkins builds with Jenkins REST API.
requirements:
- "python-jenkins >= 0.4.12"
author:
- Juan Casanova (@juanmcasanova)
extends_documentation_fragment:
- community.general.attributes
- community.general.attributes.info_module
options:
name:
description:
- Name of the Jenkins job to which the build belongs.
required: true
type: str
build_number:
description:
- An integer which specifies a build of a job.
- If not specified the last build information will be returned.
type: int
password:
description:
- Password to authenticate with the Jenkins server.
type: str
token:
description:
- API token used to authenticate with the Jenkins server.
type: str
url:
description:
- URL of the Jenkins server.
default: http://localhost:8080
type: str
user:
description:
- User to authenticate with the Jenkins server.
type: str
'''

EXAMPLES = '''
- name: Get information about a jenkins build using basic authentication
community.general.jenkins_build_info:
name: "test-check"
build_number: 1
user: admin
password: asdfg
url: http://localhost:8080

- name: Get information about a jenkins build anonymously
community.general.jenkins_build_info:
name: "stop-check"
build_number: 3
url: http://localhost:8080

- name: Get information about a jenkins build using token authentication
community.general.jenkins_build_info:
name: "delete-experiment"
build_number: 30
user: Jenkins
token: abcdefghijklmnopqrstuvwxyz123456
url: http://localhost:8080
'''

RETURN = '''
---
name:
description: Name of the jenkins job.
returned: success
type: str
sample: "test-job"
state:
description: State of the jenkins job.
returned: success
type: str
sample: present
user:
description: User used for authentication.
returned: success
type: str
sample: admin
url:
description: URL to connect to the Jenkins server.
returned: success
type: str
sample: https://jenkins.mydomain.com
build_info:
description: Build info of the jenkins job.
returned: success
type: dict
'''

import traceback

JENKINS_IMP_ERR = None
try:
import jenkins
python_jenkins_installed = True
except ImportError:
JENKINS_IMP_ERR = traceback.format_exc()
python_jenkins_installed = False

from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native


class JenkinsBuildInfo:

def __init__(self, module):
self.module = module

self.name = module.params.get('name')
self.password = module.params.get('password')
self.token = module.params.get('token')
self.user = module.params.get('user')
self.jenkins_url = module.params.get('url')
self.build_number = module.params.get('build_number')
self.server = self.get_jenkins_connection()

self.result = {
'changed': False,
'url': self.jenkins_url,
'name': self.name,
'user': self.user,
}

def get_jenkins_connection(self):
try:
if (self.user and self.password):
return jenkins.Jenkins(self.jenkins_url, self.user, self.password)
elif (self.user and self.token):
return jenkins.Jenkins(self.jenkins_url, self.user, self.token)
elif (self.user and not (self.password or self.token)):
return jenkins.Jenkins(self.jenkins_url, self.user)
else:
return jenkins.Jenkins(self.jenkins_url)
except Exception as e:
self.module.fail_json(msg='Unable to connect to Jenkins server, %s' % to_native(e))

def get_build_status(self):
try:
if self.build_number is None:
job_info = self.server.get_job_info(self.name)
self.build_number = job_info['lastBuild']['number']

return self.server.get_build_info(self.name, self.build_number)
except jenkins.JenkinsException as e:
response = {}
response["result"] = "ABSENT"
return response
except Exception as e:
self.module.fail_json(msg='Unable to fetch build information, %s' % to_native(e),
exception=traceback.format_exc())

def get_result(self):
result = self.result
build_status = self.get_build_status()

if build_status['result'] == "ABSENT":
result['failed'] = True
result['build_info'] = build_status

return result


def test_dependencies(module):
if not python_jenkins_installed:
module.fail_json(
msg=missing_required_lib("python-jenkins",
url="https://python-jenkins.readthedocs.io/en/latest/install.html"),
exception=JENKINS_IMP_ERR)


def main():
module = AnsibleModule(
argument_spec=dict(
build_number=dict(type='int'),
name=dict(required=True),
password=dict(no_log=True),
token=dict(no_log=True),
url=dict(default="http://localhost:8080"),
user=dict(),
),
mutually_exclusive=[['password', 'token']],
supports_check_mode=True,
)

test_dependencies(module)
jenkins_build_info = JenkinsBuildInfo(module)

result = jenkins_build_info.get_result()
module.exit_json(**result)


if __name__ == '__main__':
main()
40 changes: 40 additions & 0 deletions tests/unit/plugins/modules/test_jenkins_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ def get_job_info(self, name):
def get_build_info(self, name, build_number):
if name == "host-delete":
raise jenkins.JenkinsException("job {0} number {1} does not exist".format(name, build_number))
elif name == "create-detached":
return {
"building": True,
"result": None
}
return {
"building": True,
"result": "SUCCESS"
Expand Down Expand Up @@ -222,3 +227,38 @@ def test_module_delete_build_again(self, jenkins_connection, test_deps):
"token": "xyz"
})
jenkins_build.main()

@patch('ansible_collections.community.general.plugins.modules.jenkins_build.test_dependencies')
@patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_jenkins_connection')
@patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_build_status')
def test_module_create_build_without_detach(self, build_status, jenkins_connection, test_deps):
test_deps.return_value = None
jenkins_connection.return_value = JenkinsMock()
build_status.return_value = JenkinsBuildMock().get_build_status()

with self.assertRaises(AnsibleExitJson) as return_json:
set_module_args({
"name": "create-detached",
"user": "abc",
"token": "xyz"
})
jenkins_build.main()

self.assertFalse(return_json.exception.args[0]['changed'])

@patch('ansible_collections.community.general.plugins.modules.jenkins_build.test_dependencies')
@patch('ansible_collections.community.general.plugins.modules.jenkins_build.JenkinsBuild.get_jenkins_connection')
def test_module_create_build_detached(self, jenkins_connection, test_deps):
test_deps.return_value = None
jenkins_connection.return_value = JenkinsMock()

with self.assertRaises(AnsibleExitJson) as return_json:
set_module_args({
"name": "create-detached",
"user": "abc",
"token": "xyz",
"detach": True
})
jenkins_build.main()

self.assertTrue(return_json.exception.args[0]['changed'])
Loading