mirror of
https://github.com/ansible-collections/community.general.git
synced 2026-04-29 01:46:53 +00:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89accbfa2b | ||
|
|
63210f4fc4 | ||
|
|
01864514c2 | ||
|
|
418589e346 | ||
|
|
88fab247ca | ||
|
|
56edbfc539 | ||
|
|
c94fa6132d | ||
|
|
2fa17c32a3 | ||
|
|
926f627128 | ||
|
|
7c6f286df2 | ||
|
|
b6ed6787b5 | ||
|
|
94a350e72b | ||
|
|
46d454eae0 | ||
|
|
adfd73d7ed | ||
|
|
aa2a5d9578 | ||
|
|
0f300bddb9 | ||
|
|
3785b656d6 | ||
|
|
16499072ff | ||
|
|
cad6b30036 | ||
|
|
2df1126d27 | ||
|
|
0d5ec37249 | ||
|
|
7c04aaa48f | ||
|
|
80113063ac | ||
|
|
1b09e8168a | ||
|
|
aadd48461c | ||
|
|
d565a20013 | ||
|
|
c69fb82ee0 | ||
|
|
cffc3dad11 | ||
|
|
a27025946b | ||
|
|
1825feb652 | ||
|
|
0c2d1eda44 | ||
|
|
d617f6919f | ||
|
|
b17cc09b07 | ||
|
|
ee7f44b09b |
9
.github/BOTMETA.yml
vendored
9
.github/BOTMETA.yml
vendored
@@ -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:
|
||||
@@ -937,7 +939,7 @@ files:
|
||||
labels: pagerduty
|
||||
maintainers: suprememoocow thaumos
|
||||
$modules/pagerduty_alert.py:
|
||||
maintainers: ApsOps
|
||||
maintainers: ApsOps xshen1
|
||||
$modules/pagerduty_change.py:
|
||||
maintainers: adamvaughan
|
||||
$modules/pagerduty_user.py:
|
||||
@@ -980,6 +982,9 @@ files:
|
||||
maintainers: $team_solaris dermute
|
||||
$modules/pmem.py:
|
||||
maintainers: mizumm
|
||||
$modules/pnpm.py:
|
||||
ignore: chrishoffman
|
||||
maintainers: aretrosen
|
||||
$modules/portage.py:
|
||||
ignore: sayap
|
||||
labels: portage
|
||||
|
||||
@@ -6,6 +6,47 @@ Community General Release Notes
|
||||
|
||||
This changelog describes changes after version 6.0.0.
|
||||
|
||||
v7.4.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
Bugfix and feature release.
|
||||
|
||||
Minor Changes
|
||||
-------------
|
||||
|
||||
- cobbler inventory plugin - add ``exclude_mgmt_classes`` and ``include_mgmt_classes`` options to exclude or include hosts based on management classes (https://github.com/ansible-collections/community.general/pull/7184).
|
||||
- cpanm - minor refactor when creating the ``CmdRunner`` object (https://github.com/ansible-collections/community.general/pull/7231).
|
||||
- gitlab_group_variable - add support for ``raw`` variables suboption (https://github.com/ansible-collections/community.general/pull/7132).
|
||||
- gitlab_project_variable - add support for ``raw`` variables suboption (https://github.com/ansible-collections/community.general/pull/7132).
|
||||
- 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).
|
||||
- make - allows ``params`` to be used without value (https://github.com/ansible-collections/community.general/pull/7180).
|
||||
- nmap inventory plugin - now has a ``use_arp_ping`` option to allow the user to disable the default ARP ping query for a more reliable form (https://github.com/ansible-collections/community.general/pull/7119).
|
||||
- pagerduty - adds in option to use v2 API for creating pagerduty incidents (https://github.com/ansible-collections/community.general/issues/6151)
|
||||
- pritunl module utils - ensure ``validate_certs`` parameter is honoured in all methods (https://github.com/ansible-collections/community.general/pull/7156).
|
||||
- redfish_info - report ``Id`` in the output of ``GetManagerInventory`` (https://github.com/ansible-collections/community.general/pull/7140).
|
||||
- redfish_utils module utils - support ``Volumes`` in response for ``GetDiskInventory`` (https://github.com/ansible-collections/community.general/pull/6819).
|
||||
- unixy callback plugin - add support for ``check_mode_markers`` option (https://github.com/ansible-collections/community.general/pull/7179).
|
||||
|
||||
Bugfixes
|
||||
--------
|
||||
|
||||
- CmdRunner module utils - does not attempt to resolve path if executable is a relative or absolute path (https://github.com/ansible-collections/community.general/pull/7200).
|
||||
- nmap inventory plugin - now uses ``get_option`` in all cases to get its configuration information (https://github.com/ansible-collections/community.general/pull/7119).
|
||||
- nsupdate - fix a possible ``list index out of range`` exception (https://github.com/ansible-collections/community.general/issues/836).
|
||||
- oci_utils module util - fix inappropriate logical comparison expressions and makes them simpler. The previous checks had logical short circuits (https://github.com/ansible-collections/community.general/pull/7125).
|
||||
- pritunl module utils - fix incorrect URL parameter for orgnization add method (https://github.com/ansible-collections/community.general/pull/7161).
|
||||
- snap - an exception was being raised when snap list was empty (https://github.com/ansible-collections/community.general/pull/7124, https://github.com/ansible-collections/community.general/issues/7120).
|
||||
|
||||
New Modules
|
||||
-----------
|
||||
|
||||
- jenkins_build_info - Get information about Jenkins builds
|
||||
- pnpm - Manage node.js packages with pnpm
|
||||
|
||||
v7.3.0
|
||||
======
|
||||
|
||||
|
||||
@@ -1375,3 +1375,66 @@ releases:
|
||||
- 7113-redfish-utils-power-cycle.yml
|
||||
- lvol-pct-of-origin.yml
|
||||
release_date: '2023-08-15'
|
||||
7.4.0:
|
||||
changes:
|
||||
bugfixes:
|
||||
- CmdRunner module utils - does not attempt to resolve path if executable is
|
||||
a relative or absolute path (https://github.com/ansible-collections/community.general/pull/7200).
|
||||
- nmap inventory plugin - now uses ``get_option`` in all cases to get its configuration
|
||||
information (https://github.com/ansible-collections/community.general/pull/7119).
|
||||
- nsupdate - fix a possible ``list index out of range`` exception (https://github.com/ansible-collections/community.general/issues/836).
|
||||
- oci_utils module util - fix inappropriate logical comparison expressions and
|
||||
makes them simpler. The previous checks had logical short circuits (https://github.com/ansible-collections/community.general/pull/7125).
|
||||
- pritunl module utils - fix incorrect URL parameter for orgnization add method
|
||||
(https://github.com/ansible-collections/community.general/pull/7161).
|
||||
- snap - an exception was being raised when snap list was empty (https://github.com/ansible-collections/community.general/pull/7124,
|
||||
https://github.com/ansible-collections/community.general/issues/7120).
|
||||
minor_changes:
|
||||
- cobbler inventory plugin - add ``exclude_mgmt_classes`` and ``include_mgmt_classes``
|
||||
options to exclude or include hosts based on management classes (https://github.com/ansible-collections/community.general/pull/7184).
|
||||
- cpanm - minor refactor when creating the ``CmdRunner`` object (https://github.com/ansible-collections/community.general/pull/7231).
|
||||
- gitlab_group_variable - add support for ``raw`` variables suboption (https://github.com/ansible-collections/community.general/pull/7132).
|
||||
- gitlab_project_variable - add support for ``raw`` variables suboption (https://github.com/ansible-collections/community.general/pull/7132).
|
||||
- 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).
|
||||
- make - allows ``params`` to be used without value (https://github.com/ansible-collections/community.general/pull/7180).
|
||||
- nmap inventory plugin - now has a ``use_arp_ping`` option to allow the user
|
||||
to disable the default ARP ping query for a more reliable form (https://github.com/ansible-collections/community.general/pull/7119).
|
||||
- pagerduty - adds in option to use v2 API for creating pagerduty incidents
|
||||
(https://github.com/ansible-collections/community.general/issues/6151)
|
||||
- pritunl module utils - ensure ``validate_certs`` parameter is honoured in
|
||||
all methods (https://github.com/ansible-collections/community.general/pull/7156).
|
||||
- redfish_info - report ``Id`` in the output of ``GetManagerInventory`` (https://github.com/ansible-collections/community.general/pull/7140).
|
||||
- redfish_utils module utils - support ``Volumes`` in response for ``GetDiskInventory``
|
||||
(https://github.com/ansible-collections/community.general/pull/6819).
|
||||
- unixy callback plugin - add support for ``check_mode_markers`` option (https://github.com/ansible-collections/community.general/pull/7179).
|
||||
release_summary: Bugfix and feature release.
|
||||
fragments:
|
||||
- 6819-redfish-utils-add-links-parameter-for-get_disk_inventory.yml
|
||||
- 7.4.0.yml
|
||||
- 7118-nmap_inv_plugin_no_arp_option.yml
|
||||
- 7124-snap-empty-list.yml
|
||||
- 7125-fix-inappropriate-comparison.yml
|
||||
- 7132-gitlab-raw-variables.yml
|
||||
- 7140-id-getmanagerinv-output.yml
|
||||
- 7156-ensure-validate-certs-parameter-is-honoured.yml
|
||||
- 7161-fix-incorrect-post-parameter.yml
|
||||
- 7179-unixy-support-checkmode-markers.yml
|
||||
- 7180-make_params_without_value.yml
|
||||
- 7184-cobbler-mgmt-classes.yml
|
||||
- 7200-cmd-runner-abs-path.yml
|
||||
- 7219-fix-nsupdate-cname.yaml
|
||||
- 7231-cpanm-adjustments.yml
|
||||
- improvements-to-jenkins-build-module.yml
|
||||
- update-v2-pagerduty-alert.yml
|
||||
modules:
|
||||
- description: Get information about Jenkins builds
|
||||
name: jenkins_build_info
|
||||
namespace: ''
|
||||
- description: Manage node.js packages with pnpm
|
||||
name: pnpm
|
||||
namespace: ''
|
||||
release_date: '2023-09-11'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
namespace: community
|
||||
name: general
|
||||
version: 7.3.0
|
||||
version: 7.4.0
|
||||
readme: README.md
|
||||
authors:
|
||||
- Ansible (https://github.com/ansible)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2017, Allyson Bowles <@akatch>
|
||||
# Copyright (c) 2023, Al Bowles <@akatch>
|
||||
# Copyright (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
|
||||
# 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
|
||||
@@ -11,7 +11,7 @@ __metaclass__ = type
|
||||
DOCUMENTATION = '''
|
||||
name: unixy
|
||||
type: stdout
|
||||
author: Allyson Bowles (@akatch)
|
||||
author: Al Bowles (@akatch)
|
||||
short_description: condensed Ansible output
|
||||
description:
|
||||
- Consolidated Ansible output in the style of LINUX/UNIX startup logs.
|
||||
@@ -40,7 +40,6 @@ class CallbackModule(CallbackModule_default):
|
||||
- Only display task names if the task runs on at least one host
|
||||
- Add option to display all hostnames on a single line in the appropriate result color (failures may have a separate line)
|
||||
- Consolidate stats display
|
||||
- Display whether run is in --check mode
|
||||
- Don't show play name if no hosts found
|
||||
'''
|
||||
|
||||
@@ -92,19 +91,31 @@ class CallbackModule(CallbackModule_default):
|
||||
def v2_playbook_on_task_start(self, task, is_conditional):
|
||||
self._get_task_display_name(task)
|
||||
if self.task_display_name is not None:
|
||||
self._display.display("%s..." % self.task_display_name)
|
||||
if task.check_mode and self.get_option('check_mode_markers'):
|
||||
self._display.display("%s (check mode)..." % self.task_display_name)
|
||||
else:
|
||||
self._display.display("%s..." % self.task_display_name)
|
||||
|
||||
def v2_playbook_on_handler_task_start(self, task):
|
||||
self._get_task_display_name(task)
|
||||
if self.task_display_name is not None:
|
||||
self._display.display("%s (via handler)... " % self.task_display_name)
|
||||
if task.check_mode and self.get_option('check_mode_markers'):
|
||||
self._display.display("%s (via handler in check mode)... " % self.task_display_name)
|
||||
else:
|
||||
self._display.display("%s (via handler)... " % self.task_display_name)
|
||||
|
||||
def v2_playbook_on_play_start(self, play):
|
||||
name = play.get_name().strip()
|
||||
if name and play.hosts:
|
||||
msg = u"\n- %s on hosts: %s -" % (name, ",".join(play.hosts))
|
||||
if play.check_mode and self.get_option('check_mode_markers'):
|
||||
if name and play.hosts:
|
||||
msg = u"\n- %s (in check mode) on hosts: %s -" % (name, ",".join(play.hosts))
|
||||
else:
|
||||
msg = u"- check mode -"
|
||||
else:
|
||||
msg = u"---"
|
||||
if name and play.hosts:
|
||||
msg = u"\n- %s on hosts: %s -" % (name, ",".join(play.hosts))
|
||||
else:
|
||||
msg = u"---"
|
||||
|
||||
self._display.display(msg)
|
||||
|
||||
@@ -227,8 +238,10 @@ class CallbackModule(CallbackModule_default):
|
||||
self._display.display(" Ran out of hosts!", color=C.COLOR_ERROR)
|
||||
|
||||
def v2_playbook_on_start(self, playbook):
|
||||
# TODO display whether this run is happening in check mode
|
||||
self._display.display("Executing playbook %s" % basename(playbook._file_name))
|
||||
if context.CLIARGS['check'] and self.get_option('check_mode_markers'):
|
||||
self._display.display("Executing playbook %s in check mode" % basename(playbook._file_name))
|
||||
else:
|
||||
self._display.display("Executing playbook %s" % basename(playbook._file_name))
|
||||
|
||||
# show CLI arguments
|
||||
if self._display.verbosity > 3:
|
||||
|
||||
@@ -42,6 +42,12 @@ DOCUMENTATION = '''
|
||||
description: Fallback to cached results if connection to cobbler fails.
|
||||
type: boolean
|
||||
default: false
|
||||
exclude_mgmt_classes:
|
||||
description: Management classes to exclude from inventory.
|
||||
type: list
|
||||
default: []
|
||||
elements: str
|
||||
version_added: 7.4.0
|
||||
exclude_profiles:
|
||||
description:
|
||||
- Profiles to exclude from inventory.
|
||||
@@ -49,6 +55,12 @@ DOCUMENTATION = '''
|
||||
type: list
|
||||
default: []
|
||||
elements: str
|
||||
include_mgmt_classes:
|
||||
description: Management classes to include from inventory.
|
||||
type: list
|
||||
default: []
|
||||
elements: str
|
||||
version_added: 7.4.0
|
||||
include_profiles:
|
||||
description:
|
||||
- Profiles to include from inventory.
|
||||
@@ -216,6 +228,8 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
|
||||
self.cache_key = self.get_cache_key(path)
|
||||
self.use_cache = cache and self.get_option('cache')
|
||||
|
||||
self.exclude_mgmt_classes = self.get_option('exclude_mgmt_classes')
|
||||
self.include_mgmt_classes = self.get_option('include_mgmt_classes')
|
||||
self.exclude_profiles = self.get_option('exclude_profiles')
|
||||
self.include_profiles = self.get_option('include_profiles')
|
||||
self.group_by = self.get_option('group_by')
|
||||
@@ -265,9 +279,16 @@ class InventoryModule(BaseInventoryPlugin, Cacheable):
|
||||
hostname = host['hostname'] # None
|
||||
interfaces = host['interfaces']
|
||||
|
||||
if self._exclude_profile(host['profile']):
|
||||
self.display.vvvv('Excluding host %s in profile %s\n' % (host['name'], host['profile']))
|
||||
continue
|
||||
if set(host['mgmt_classes']) & set(self.include_mgmt_classes):
|
||||
self.display.vvvv('Including host %s in mgmt_classes %s\n' % (host['name'], host['mgmt_classes']))
|
||||
else:
|
||||
if self._exclude_profile(host['profile']):
|
||||
self.display.vvvv('Excluding host %s in profile %s\n' % (host['name'], host['profile']))
|
||||
continue
|
||||
|
||||
if set(host['mgmt_classes']) & set(self.exclude_mgmt_classes):
|
||||
self.display.vvvv('Excluding host %s in mgmt_classes %s\n' % (host['name'], host['mgmt_classes']))
|
||||
continue
|
||||
|
||||
# hostname is often empty for non-static IP hosts
|
||||
if hostname == '':
|
||||
|
||||
@@ -85,6 +85,11 @@ DOCUMENTATION = '''
|
||||
type: boolean
|
||||
default: false
|
||||
version_added: 6.1.0
|
||||
use_arp_ping:
|
||||
description: Whether to always (V(true)) use the quick ARP ping or (V(false)) a slower but more reliable method.
|
||||
type: boolean
|
||||
default: true
|
||||
version_added: 7.4.0
|
||||
notes:
|
||||
- At least one of ipv4 or ipv6 is required to be True, both can be True, but they cannot both be False.
|
||||
- 'TODO: add OS fingerprinting'
|
||||
@@ -196,40 +201,43 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable):
|
||||
# setup command
|
||||
cmd = [self._nmap]
|
||||
|
||||
if self._options['sudo']:
|
||||
if self.get_option['sudo']:
|
||||
cmd.insert(0, 'sudo')
|
||||
|
||||
if self._options['port']:
|
||||
if self.get_option['port']:
|
||||
cmd.append('-p')
|
||||
cmd.append(self._options['port'])
|
||||
cmd.append(self.get_option['port'])
|
||||
|
||||
if not self._options['ports']:
|
||||
if not self.get_option['ports']:
|
||||
cmd.append('-sP')
|
||||
|
||||
if self._options['ipv4'] and not self._options['ipv6']:
|
||||
if self.get_option['ipv4'] and not self.get_option['ipv6']:
|
||||
cmd.append('-4')
|
||||
elif self._options['ipv6'] and not self._options['ipv4']:
|
||||
elif self.get_option['ipv6'] and not self.get_option['ipv4']:
|
||||
cmd.append('-6')
|
||||
elif not self._options['ipv6'] and not self._options['ipv4']:
|
||||
elif not self.get_option['ipv6'] and not self.get_option['ipv4']:
|
||||
raise AnsibleParserError('One of ipv4 or ipv6 must be enabled for this plugin')
|
||||
|
||||
if self._options['exclude']:
|
||||
if self.get_option['exclude']:
|
||||
cmd.append('--exclude')
|
||||
cmd.append(','.join(self._options['exclude']))
|
||||
cmd.append(','.join(self.get_option['exclude']))
|
||||
|
||||
if self._options['dns_resolve']:
|
||||
if self.get_option['dns_resolve']:
|
||||
cmd.append('-n')
|
||||
|
||||
if self._options['udp_scan']:
|
||||
if self.get_option['udp_scan']:
|
||||
cmd.append('-sU')
|
||||
|
||||
if self._options['icmp_timestamp']:
|
||||
if self.get_option['icmp_timestamp']:
|
||||
cmd.append('-PP')
|
||||
|
||||
if self._options['open']:
|
||||
if self.get_option['open']:
|
||||
cmd.append('--open')
|
||||
|
||||
cmd.append(self._options['address'])
|
||||
if not self.get_option['use_arp_ping']:
|
||||
cmd.append('--disable-arp-ping')
|
||||
|
||||
cmd.append(self.get_option['address'])
|
||||
try:
|
||||
# execute
|
||||
p = Popen(cmd, stdout=PIPE, stderr=PIPE)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
from functools import wraps
|
||||
|
||||
from ansible.module_utils.common.collections import is_sequence
|
||||
@@ -204,12 +205,17 @@ class CmdRunner(object):
|
||||
environ_update = {}
|
||||
self.environ_update = environ_update
|
||||
|
||||
self.command[0] = module.get_bin_path(self.command[0], opt_dirs=path_prefix, required=True)
|
||||
_cmd = self.command[0]
|
||||
self.command[0] = _cmd if (os.path.isabs(_cmd) or '/' in _cmd) else module.get_bin_path(_cmd, opt_dirs=path_prefix, required=True)
|
||||
|
||||
for mod_param_name, spec in iteritems(module.argument_spec):
|
||||
if mod_param_name not in self.arg_formats:
|
||||
self.arg_formats[mod_param_name] = _Format.as_default_type(spec.get('type', 'str'), mod_param_name)
|
||||
|
||||
@property
|
||||
def binary(self):
|
||||
return self.command[0]
|
||||
|
||||
def __call__(self, args_order=None, output_process=None, ignore_value_none=True, check_mode_skip=False, check_mode_return=None, **kwargs):
|
||||
if output_process is None:
|
||||
output_process = _process_as_is
|
||||
|
||||
@@ -135,6 +135,7 @@ def vars_to_variables(vars, module):
|
||||
"value": str(value),
|
||||
"masked": False,
|
||||
"protected": False,
|
||||
"raw": False,
|
||||
"variable_type": "env_var",
|
||||
}
|
||||
)
|
||||
@@ -145,6 +146,7 @@ def vars_to_variables(vars, module):
|
||||
"value": value.get('value'),
|
||||
"masked": value.get('masked'),
|
||||
"protected": value.get('protected'),
|
||||
"raw": value.get('raw'),
|
||||
"variable_type": value.get('variable_type'),
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ def _post_pritunl_organization(
|
||||
api_secret=api_secret,
|
||||
base_url=base_url,
|
||||
method="POST",
|
||||
path="/organization/%s",
|
||||
path="/organization",
|
||||
headers={"Content-Type": "application/json"},
|
||||
data=json.dumps(organization_data),
|
||||
validate_certs=validate_certs,
|
||||
@@ -220,7 +220,7 @@ def post_pritunl_organization(
|
||||
api_secret=api_secret,
|
||||
base_url=base_url,
|
||||
organization_data={"name": organization_name},
|
||||
validate_certs=True,
|
||||
validate_certs=validate_certs,
|
||||
)
|
||||
|
||||
if response.getcode() != 200:
|
||||
@@ -248,7 +248,7 @@ def post_pritunl_user(
|
||||
base_url=base_url,
|
||||
organization_id=organization_id,
|
||||
user_data=user_data,
|
||||
validate_certs=True,
|
||||
validate_certs=validate_certs,
|
||||
)
|
||||
|
||||
if response.getcode() != 200:
|
||||
@@ -267,7 +267,7 @@ def post_pritunl_user(
|
||||
organization_id=organization_id,
|
||||
user_data=user_data,
|
||||
user_id=user_id,
|
||||
validate_certs=True,
|
||||
validate_certs=validate_certs,
|
||||
)
|
||||
|
||||
if response.getcode() != 200:
|
||||
@@ -287,7 +287,7 @@ def delete_pritunl_organization(
|
||||
api_secret=api_secret,
|
||||
base_url=base_url,
|
||||
organization_id=organization_id,
|
||||
validate_certs=True,
|
||||
validate_certs=validate_certs,
|
||||
)
|
||||
|
||||
if response.getcode() != 200:
|
||||
@@ -307,7 +307,7 @@ def delete_pritunl_user(
|
||||
base_url=base_url,
|
||||
organization_id=organization_id,
|
||||
user_id=user_id,
|
||||
validate_certs=True,
|
||||
validate_certs=validate_certs,
|
||||
)
|
||||
|
||||
if response.getcode() != 200:
|
||||
|
||||
@@ -561,7 +561,7 @@ def are_lists_equal(s, t):
|
||||
if s is None and t is None:
|
||||
return True
|
||||
|
||||
if (s is None and len(t) >= 0) or (t is None and len(s) >= 0) or (len(s) != len(t)):
|
||||
if s is None or t is None or (len(s) != len(t)):
|
||||
return False
|
||||
|
||||
if len(s) == 0:
|
||||
@@ -1026,10 +1026,7 @@ def check_if_user_value_matches_resources_attr(
|
||||
return
|
||||
|
||||
if (
|
||||
resources_value_for_attr is None
|
||||
and len(user_provided_value_for_attr) >= 0
|
||||
or user_provided_value_for_attr is None
|
||||
and len(resources_value_for_attr) >= 0
|
||||
resources_value_for_attr is None or user_provided_value_for_attr is None
|
||||
):
|
||||
res[0] = False
|
||||
return
|
||||
|
||||
@@ -791,7 +791,7 @@ class RedfishUtils(object):
|
||||
properties = ['BlockSizeBytes', 'CapableSpeedGbs', 'CapacityBytes',
|
||||
'EncryptionAbility', 'EncryptionStatus',
|
||||
'FailurePredicted', 'HotspareType', 'Id', 'Identifiers',
|
||||
'Manufacturer', 'MediaType', 'Model', 'Name',
|
||||
'Links', 'Manufacturer', 'MediaType', 'Model', 'Name',
|
||||
'PartNumber', 'PhysicalLocation', 'Protocol', 'Revision',
|
||||
'RotationSpeedRPM', 'SerialNumber', 'Status']
|
||||
|
||||
@@ -861,7 +861,12 @@ class RedfishUtils(object):
|
||||
for property in properties:
|
||||
if property in data:
|
||||
if data[property] is not None:
|
||||
drive_result[property] = data[property]
|
||||
if property == "Links":
|
||||
if "Volumes" in data["Links"].keys():
|
||||
volumes = [v["@odata.id"] for v in data["Links"]["Volumes"]]
|
||||
drive_result["Volumes"] = volumes
|
||||
else:
|
||||
drive_result[property] = data[property]
|
||||
drive_results.append(drive_result)
|
||||
drives = {'Controller': controller_name,
|
||||
'Drives': drive_results}
|
||||
@@ -3344,7 +3349,7 @@ class RedfishUtils(object):
|
||||
result = {}
|
||||
inventory = {}
|
||||
# Get these entries, but does not fail if not found
|
||||
properties = ['FirmwareVersion', 'ManagerType', 'Manufacturer', 'Model',
|
||||
properties = ['Id', 'FirmwareVersion', 'ManagerType', 'Manufacturer', 'Model',
|
||||
'PartNumber', 'PowerState', 'SerialNumber', 'Status', 'UUID']
|
||||
|
||||
response = self.get_request(self.root_uri + manager_uri)
|
||||
|
||||
@@ -154,7 +154,7 @@ def _get_ctl_binary(module):
|
||||
if ctl_binary is not None:
|
||||
return ctl_binary
|
||||
|
||||
module.fail_json(msg="Neither of apache2ctl nor apachctl found. At least one apache control binary is necessary.")
|
||||
module.fail_json(msg="Neither of apache2ctl nor apachectl found. At least one apache control binary is necessary.")
|
||||
|
||||
|
||||
def _module_is_enabled(module):
|
||||
|
||||
@@ -183,8 +183,9 @@ class CPANMinus(ModuleHelper):
|
||||
if v.name and v.from_path:
|
||||
self.do_raise("Parameters 'name' and 'from_path' are mutually exclusive when 'mode=new'")
|
||||
|
||||
self.command = self.get_bin_path(v.executable if v.executable else self.command)
|
||||
self.vars.set("binary", self.command)
|
||||
self.command = v.executable if v.executable else self.command
|
||||
self.runner = CmdRunner(self.module, self.command, self.command_args_formats, check_rc=True)
|
||||
self.vars.binary = self.runner.binary
|
||||
|
||||
def _is_package_installed(self, name, locallib, version):
|
||||
def process(rc, out, err):
|
||||
@@ -220,8 +221,6 @@ class CPANMinus(ModuleHelper):
|
||||
self.do_raise(msg=err, cmd=self.vars.cmd_args)
|
||||
return 'is up to date' not in err and 'is up to date' not in out
|
||||
|
||||
runner = CmdRunner(self.module, self.command, self.command_args_formats, check_rc=True)
|
||||
|
||||
v = self.vars
|
||||
pkg_param = 'from_path' if v.from_path else 'name'
|
||||
|
||||
@@ -235,7 +234,7 @@ class CPANMinus(ModuleHelper):
|
||||
return
|
||||
pkg_spec = self.sanitize_pkg_spec_version(v[pkg_param], v.version)
|
||||
|
||||
with runner(['notest', 'locallib', 'mirror', 'mirror_only', 'installdeps', 'pkg_spec'], output_process=process) as ctx:
|
||||
with self.runner(['notest', 'locallib', 'mirror', 'mirror_only', 'installdeps', 'pkg_spec'], output_process=process) as ctx:
|
||||
self.changed = ctx.run(pkg_spec=pkg_spec)
|
||||
|
||||
|
||||
|
||||
@@ -53,13 +53,14 @@ options:
|
||||
type: bool
|
||||
vars:
|
||||
description:
|
||||
- When the list element is a simple key-value pair, set masked and protected to false.
|
||||
- When the list element is a dict with the keys C(value), C(masked) and C(protected), the user can
|
||||
have full control about whether a value should be masked, protected or both.
|
||||
- When the list element is a simple key-value pair, masked, raw and protected will be set to false.
|
||||
- When the list element is a dict with the keys C(value), C(masked), C(raw) and C(protected), the user can
|
||||
have full control about whether a value should be masked, raw, protected or both.
|
||||
- Support for group variables requires GitLab >= 9.5.
|
||||
- Support for environment_scope requires GitLab Premium >= 13.11.
|
||||
- Support for protected values requires GitLab >= 9.3.
|
||||
- Support for masked values requires GitLab >= 11.10.
|
||||
- Support for raw values requires GitLab >= 15.7.
|
||||
- A C(value) must be a string or a number.
|
||||
- Field C(variable_type) must be a string with either V(env_var), which is the default, or V(file).
|
||||
- When a value is masked, it must be in Base64 and have a length of at least 8 characters.
|
||||
@@ -95,6 +96,13 @@ options:
|
||||
- Wether variable value is protected or not.
|
||||
type: bool
|
||||
default: false
|
||||
raw:
|
||||
description:
|
||||
- Wether variable value is raw or not.
|
||||
- Support for raw values requires GitLab >= 15.7.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: '7.4.0'
|
||||
variable_type:
|
||||
description:
|
||||
- Wether a variable is an environment variable (V(env_var)) or a file (V(file)).
|
||||
@@ -126,6 +134,38 @@ EXAMPLES = r'''
|
||||
variable_type: env_var
|
||||
environment_scope: production
|
||||
|
||||
- name: Set or update some CI/CD variables with raw value
|
||||
community.general.gitlab_group_variable:
|
||||
api_url: https://gitlab.com
|
||||
api_token: secret_access_token
|
||||
group: scodeman/testgroup/
|
||||
purge: false
|
||||
vars:
|
||||
ACCESS_KEY_ID: abc123
|
||||
SECRET_ACCESS_KEY:
|
||||
value: 3214cbad
|
||||
masked: true
|
||||
protected: true
|
||||
raw: true
|
||||
variable_type: env_var
|
||||
environment_scope: '*'
|
||||
|
||||
- name: Set or update some CI/CD variables with expandable value
|
||||
community.general.gitlab_group_variable:
|
||||
api_url: https://gitlab.com
|
||||
api_token: secret_access_token
|
||||
group: scodeman/testgroup/
|
||||
purge: false
|
||||
vars:
|
||||
ACCESS_KEY_ID: abc123
|
||||
SECRET_ACCESS_KEY:
|
||||
value: '$MY_OTHER_VARIABLE'
|
||||
masked: true
|
||||
protected: true
|
||||
raw: false
|
||||
variable_type: env_var
|
||||
environment_scope: '*'
|
||||
|
||||
- name: Delete one variable
|
||||
community.general.gitlab_group_variable:
|
||||
api_url: https://gitlab.com
|
||||
@@ -199,6 +239,7 @@ class GitlabGroupVariables(object):
|
||||
"value": var_obj.get('value'),
|
||||
"masked": var_obj.get('masked'),
|
||||
"protected": var_obj.get('protected'),
|
||||
"raw": var_obj.get('raw'),
|
||||
"variable_type": var_obj.get('variable_type'),
|
||||
}
|
||||
if var_obj.get('environment_scope') is not None:
|
||||
@@ -267,6 +308,8 @@ def native_python_main(this_gitlab, purge, requested_variables, state, module):
|
||||
item['value'] = str(item.get('value'))
|
||||
if item.get('protected') is None:
|
||||
item['protected'] = False
|
||||
if item.get('raw') is None:
|
||||
item['raw'] = False
|
||||
if item.get('masked') is None:
|
||||
item['masked'] = False
|
||||
if item.get('environment_scope') is None:
|
||||
@@ -343,6 +386,7 @@ def main():
|
||||
value=dict(type='str', no_log=True),
|
||||
masked=dict(type='bool', default=False),
|
||||
protected=dict(type='bool', default=False),
|
||||
raw=dict(type='bool', default=False),
|
||||
environment_scope=dict(type='str', default='*'),
|
||||
variable_type=dict(type='str', default='env_var', choices=["env_var", "file"])
|
||||
)),
|
||||
|
||||
@@ -51,11 +51,12 @@ options:
|
||||
type: bool
|
||||
vars:
|
||||
description:
|
||||
- When the list element is a simple key-value pair, masked and protected will be set to false.
|
||||
- When the list element is a dict with the keys C(value), C(masked) and C(protected), the user can
|
||||
have full control about whether a value should be masked, protected or both.
|
||||
- When the list element is a simple key-value pair, masked, raw and protected will be set to false.
|
||||
- When the list element is a dict with the keys C(value), C(masked), C(raw) and C(protected), the user can
|
||||
have full control about whether a value should be masked, raw, protected or both.
|
||||
- Support for protected values requires GitLab >= 9.3.
|
||||
- Support for masked values requires GitLab >= 11.10.
|
||||
- Support for raw values requires GitLab >= 15.7.
|
||||
- Support for environment_scope requires GitLab Premium >= 13.11.
|
||||
- Support for variable_type requires GitLab >= 11.11.
|
||||
- A C(value) must be a string or a number.
|
||||
@@ -96,6 +97,13 @@ options:
|
||||
- Support for protected values requires GitLab >= 9.3.
|
||||
type: bool
|
||||
default: false
|
||||
raw:
|
||||
description:
|
||||
- Wether variable value is raw or not.
|
||||
- Support for raw values requires GitLab >= 15.7.
|
||||
type: bool
|
||||
default: false
|
||||
version_added: '7.4.0'
|
||||
variable_type:
|
||||
description:
|
||||
- Wether a variable is an environment variable (V(env_var)) or a file (V(file)).
|
||||
@@ -143,6 +151,38 @@ EXAMPLES = '''
|
||||
variable_type: env_var
|
||||
environment_scope: '*'
|
||||
|
||||
- name: Set or update some CI/CD variables with raw value
|
||||
community.general.gitlab_project_variable:
|
||||
api_url: https://gitlab.com
|
||||
api_token: secret_access_token
|
||||
project: markuman/dotfiles
|
||||
purge: false
|
||||
vars:
|
||||
ACCESS_KEY_ID: abc123
|
||||
SECRET_ACCESS_KEY:
|
||||
value: 3214cbad
|
||||
masked: true
|
||||
protected: true
|
||||
raw: true
|
||||
variable_type: env_var
|
||||
environment_scope: '*'
|
||||
|
||||
- name: Set or update some CI/CD variables with expandable value
|
||||
community.general.gitlab_project_variable:
|
||||
api_url: https://gitlab.com
|
||||
api_token: secret_access_token
|
||||
project: markuman/dotfiles
|
||||
purge: false
|
||||
vars:
|
||||
ACCESS_KEY_ID: abc123
|
||||
SECRET_ACCESS_KEY:
|
||||
value: '$MY_OTHER_VARIABLE'
|
||||
masked: true
|
||||
protected: true
|
||||
raw: false
|
||||
variable_type: env_var
|
||||
environment_scope: '*'
|
||||
|
||||
- name: Delete one variable
|
||||
community.general.gitlab_project_variable:
|
||||
api_url: https://gitlab.com
|
||||
@@ -220,6 +260,7 @@ class GitlabProjectVariables(object):
|
||||
"value": var_obj.get('value'),
|
||||
"masked": var_obj.get('masked'),
|
||||
"protected": var_obj.get('protected'),
|
||||
"raw": var_obj.get('raw'),
|
||||
"variable_type": var_obj.get('variable_type'),
|
||||
}
|
||||
|
||||
@@ -290,6 +331,8 @@ def native_python_main(this_gitlab, purge, requested_variables, state, module):
|
||||
item['value'] = str(item.get('value'))
|
||||
if item.get('protected') is None:
|
||||
item['protected'] = False
|
||||
if item.get('raw') is None:
|
||||
item['raw'] = False
|
||||
if item.get('masked') is None:
|
||||
item['masked'] = False
|
||||
if item.get('environment_scope') is None:
|
||||
@@ -366,6 +409,7 @@ def main():
|
||||
value=dict(type='str', no_log=True),
|
||||
masked=dict(type='bool', default=False),
|
||||
protected=dict(type='bool', default=False),
|
||||
raw=dict(type='bool', default=False),
|
||||
environment_scope=dict(type='str', default='*'),
|
||||
variable_type=dict(type='str', default='env_var', choices=["env_var", "file"]),
|
||||
)),
|
||||
|
||||
@@ -20,6 +20,7 @@ requirements:
|
||||
author:
|
||||
- Brett Milford (@brettmilford)
|
||||
- Tong He (@unnecessary-username)
|
||||
- Juan Casanova (@juanmcasanova)
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
@@ -65,6 +66,19 @@ options:
|
||||
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 = '''
|
||||
@@ -152,6 +166,8 @@ class JenkinsBuild:
|
||||
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 = {
|
||||
@@ -235,7 +251,14 @@ class JenkinsBuild:
|
||||
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":
|
||||
@@ -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]],
|
||||
@@ -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)
|
||||
|
||||
|
||||
210
plugins/modules/jenkins_build_info.py
Normal file
210
plugins/modules/jenkins_build_info.py
Normal file
@@ -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()
|
||||
@@ -67,7 +67,7 @@ author:
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Set default client scopes on realm level
|
||||
community.general.keycloak_clientsecret_info:
|
||||
community.general.keycloak_clientscope_type:
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: https://auth.example.com/auth
|
||||
auth_realm: master
|
||||
@@ -79,7 +79,7 @@ EXAMPLES = '''
|
||||
|
||||
|
||||
- name: Set default and optional client scopes on client level with token auth
|
||||
community.general.keycloak_clientsecret_info:
|
||||
community.general.keycloak_clientscope_type:
|
||||
auth_client_id: admin-cli
|
||||
auth_keycloak_url: https://auth.example.com/auth
|
||||
token: TOKEN
|
||||
|
||||
@@ -49,6 +49,7 @@ options:
|
||||
params:
|
||||
description:
|
||||
- Any extra parameters to pass to make.
|
||||
- If the value is empty, only the key will be used. For example, V(FOO:) will produce V(FOO), not V(FOO=).
|
||||
type: dict
|
||||
target:
|
||||
description:
|
||||
@@ -90,6 +91,18 @@ EXAMPLES = r'''
|
||||
chdir: /home/ubuntu/cool-project
|
||||
target: all
|
||||
file: /some-project/Makefile
|
||||
|
||||
- name: build arm64 kernel on FreeBSD, with 16 parallel jobs
|
||||
community.general.make:
|
||||
chdir: /usr/src
|
||||
jobs: 16
|
||||
target: buildkernel
|
||||
params:
|
||||
# This adds -DWITH_FDT to the command line:
|
||||
-DWITH_FDT:
|
||||
# The following adds TARGET=arm64 TARGET_ARCH=aarch64 to the command line:
|
||||
TARGET: arm64
|
||||
TARGET_ARCH: aarch64
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
@@ -190,7 +203,7 @@ def main():
|
||||
# Fall back to system make
|
||||
make_path = module.get_bin_path('make', required=True)
|
||||
if module.params['params'] is not None:
|
||||
make_parameters = [k + '=' + str(v) for k, v in iteritems(module.params['params'])]
|
||||
make_parameters = [k + (('=' + str(v)) if v is not None else '') for k, v in iteritems(module.params['params'])]
|
||||
else:
|
||||
make_parameters = []
|
||||
|
||||
|
||||
@@ -467,10 +467,8 @@ class RecordManager(object):
|
||||
if lookup.rcode() != dns.rcode.NOERROR:
|
||||
self.module.fail_json(msg='Failed to lookup TTL of existing matching record.')
|
||||
|
||||
if self.module.params['type'] == 'NS':
|
||||
current_ttl = lookup.answer[0].ttl if lookup.answer else lookup.authority[0].ttl
|
||||
else:
|
||||
current_ttl = lookup.answer[0].ttl
|
||||
current_ttl = lookup.answer[0].ttl if lookup.answer else lookup.authority[0].ttl
|
||||
|
||||
return current_ttl != self.module.params['ttl']
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ description:
|
||||
- This module will let you trigger, acknowledge or resolve a PagerDuty incident by sending events
|
||||
author:
|
||||
- "Amanpreet Singh (@ApsOps)"
|
||||
- "Xiao Shen (@xshen1)"
|
||||
requirements:
|
||||
- PagerDuty API access
|
||||
extends_documentation_fragment:
|
||||
@@ -30,20 +31,25 @@ options:
|
||||
type: str
|
||||
description:
|
||||
- PagerDuty unique subdomain. Obsolete. It is not used with PagerDuty REST v2 API.
|
||||
api_key:
|
||||
type: str
|
||||
description:
|
||||
- The pagerduty API key (readonly access), generated on the pagerduty site.
|
||||
- Required if O(api_version=v1).
|
||||
integration_key:
|
||||
type: str
|
||||
description:
|
||||
- The GUID of one of your 'Generic API' services.
|
||||
- This is the 'integration key' listed on a 'Integrations' tab of PagerDuty service.
|
||||
service_id:
|
||||
type: str
|
||||
description:
|
||||
- ID of PagerDuty service when incidents will be triggered, acknowledged or resolved.
|
||||
required: true
|
||||
- Required if O(api_version=v1).
|
||||
service_key:
|
||||
type: str
|
||||
description:
|
||||
- The GUID of one of your "Generic API" services. Obsolete. Please use O(integration_key).
|
||||
integration_key:
|
||||
type: str
|
||||
description:
|
||||
- The GUID of one of your "Generic API" services.
|
||||
- This is the "integration key" listed on a "Integrations" tab of PagerDuty service.
|
||||
- The GUID of one of your 'Generic API' services. Obsolete. Please use O(integration_key).
|
||||
state:
|
||||
type: str
|
||||
description:
|
||||
@@ -53,30 +59,17 @@ options:
|
||||
- 'triggered'
|
||||
- 'acknowledged'
|
||||
- 'resolved'
|
||||
api_key:
|
||||
api_version:
|
||||
type: str
|
||||
description:
|
||||
- The pagerduty API key (readonly access), generated on the pagerduty site.
|
||||
required: true
|
||||
desc:
|
||||
type: str
|
||||
description:
|
||||
- For O(state=triggered) - Required. Short description of the problem that led to this trigger. This field (or a truncated version)
|
||||
will be used when generating phone calls, SMS messages and alert emails. It will also appear on the incidents tables in the PagerDuty UI.
|
||||
The maximum length is 1024 characters.
|
||||
- For O(state=acknowledged) or O(state=resolved) - Text that will appear in the incident's log associated with this event.
|
||||
required: false
|
||||
default: Created via Ansible
|
||||
incident_key:
|
||||
type: str
|
||||
description:
|
||||
- Identifies the incident to which this O(state) should be applied.
|
||||
- For O(state=triggered) - If there's no open (i.e. unresolved) incident with this key, a new one will be created. If there's already an
|
||||
open incident with a matching key, this event will be appended to that incident's log. The event key provides an easy way to "de-dup"
|
||||
problem reports.
|
||||
- For O(state=acknowledged) or O(state=resolved) - This should be the incident_key you received back when the incident was first opened by a
|
||||
trigger event. Acknowledge events referencing resolved or nonexistent incidents will be discarded.
|
||||
required: false
|
||||
- The API version we want to use to run the module.
|
||||
- V1 is more limited with option we can provide to trigger incident.
|
||||
- V2 has more variables for example, O(severity), O(source), O(custom_details), etc.
|
||||
default: 'v1'
|
||||
choices:
|
||||
- 'v1'
|
||||
- 'v2'
|
||||
version_added: 7.4.0
|
||||
client:
|
||||
type: str
|
||||
description:
|
||||
@@ -87,6 +80,75 @@ options:
|
||||
description:
|
||||
- The URL of the monitoring client that is triggering this event.
|
||||
required: false
|
||||
component:
|
||||
type: str
|
||||
description:
|
||||
- Component of the source machine that is responsible for the event, for example C(mysql) or C(eth0).
|
||||
required: false
|
||||
version_added: 7.4.0
|
||||
custom_details:
|
||||
type: dict
|
||||
description:
|
||||
- Additional details about the event and affected system.
|
||||
- A dictionary with custom keys and values.
|
||||
required: false
|
||||
version_added: 7.4.0
|
||||
desc:
|
||||
type: str
|
||||
description:
|
||||
- For O(state=triggered) - Required. Short description of the problem that led to this trigger. This field (or a truncated version)
|
||||
will be used when generating phone calls, SMS messages and alert emails. It will also appear on the incidents tables in the PagerDuty UI.
|
||||
The maximum length is 1024 characters.
|
||||
- For O(state=acknowledged) or O(state=resolved) - Text that will appear in the incident's log associated with this event.
|
||||
required: false
|
||||
default: Created via Ansible
|
||||
incident_class:
|
||||
type: str
|
||||
description:
|
||||
- The class/type of the event, for example C(ping failure) or C(cpu load).
|
||||
required: false
|
||||
version_added: 7.4.0
|
||||
incident_key:
|
||||
type: str
|
||||
description:
|
||||
- Identifies the incident to which this O(state) should be applied.
|
||||
- For O(state=triggered) - If there's no open (i.e. unresolved) incident with this key, a new one will be created. If there's already an
|
||||
open incident with a matching key, this event will be appended to that incident's log. The event key provides an easy way to 'de-dup'
|
||||
problem reports. If no O(incident_key) is provided, then it will be generated by PagerDuty.
|
||||
- For O(state=acknowledged) or O(state=resolved) - This should be the incident_key you received back when the incident was first opened by a
|
||||
trigger event. Acknowledge events referencing resolved or nonexistent incidents will be discarded.
|
||||
required: false
|
||||
link_url:
|
||||
type: str
|
||||
description:
|
||||
- Relevant link url to the alert. For example, the website or the job link.
|
||||
required: false
|
||||
version_added: 7.4.0
|
||||
link_text:
|
||||
type: str
|
||||
description:
|
||||
- A short decription of the link_url.
|
||||
required: false
|
||||
version_added: 7.4.0
|
||||
source:
|
||||
type: str
|
||||
description:
|
||||
- The unique location of the affected system, preferably a hostname or FQDN.
|
||||
- Required in case of O(state=trigger) and O(api_version=v2).
|
||||
required: false
|
||||
version_added: 7.4.0
|
||||
severity:
|
||||
type: str
|
||||
description:
|
||||
- The perceived severity of the status the event is describing with respect to the affected system.
|
||||
- Required in case of O(state=trigger) and O(api_version=v2).
|
||||
default: 'critical'
|
||||
choices:
|
||||
- 'critical'
|
||||
- 'warning'
|
||||
- 'error'
|
||||
- 'info'
|
||||
version_added: 7.4.0
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
@@ -127,12 +189,50 @@ EXAMPLES = '''
|
||||
state: resolved
|
||||
incident_key: somekey
|
||||
desc: "some text for incident's log"
|
||||
|
||||
- name: Trigger an v2 incident with just the basic options
|
||||
community.general.pagerduty_alert:
|
||||
integration_key: xxx
|
||||
api_version: v2
|
||||
source: My Ansible Script
|
||||
state: triggered
|
||||
desc: problem that led to this trigger
|
||||
|
||||
- name: Trigger an v2 incident with more options
|
||||
community.general.pagerduty_alert:
|
||||
integration_key: xxx
|
||||
api_version: v2
|
||||
source: My Ansible Script
|
||||
state: triggered
|
||||
desc: problem that led to this trigger
|
||||
incident_key: somekey
|
||||
client: Sample Monitoring Service
|
||||
client_url: http://service.example.com
|
||||
component: mysql
|
||||
incident_class: ping failure
|
||||
link_url: https://pagerduty.com
|
||||
link_text: PagerDuty
|
||||
|
||||
- name: Acknowledge an incident based on incident_key using v2
|
||||
community.general.pagerduty_alert:
|
||||
api_version: v2
|
||||
integration_key: xxx
|
||||
incident_key: somekey
|
||||
state: acknowledged
|
||||
|
||||
- name: Resolve an incident based on incident_key
|
||||
community.general.pagerduty_alert:
|
||||
api_version: v2
|
||||
integration_key: xxx
|
||||
incident_key: somekey
|
||||
state: resolved
|
||||
'''
|
||||
import json
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.urls import fetch_url
|
||||
from ansible.module_utils.six.moves.urllib.parse import urlparse, urlencode, urlunparse
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def check(module, name, state, service_id, integration_key, api_key, incident_key=None, http_call=fetch_url):
|
||||
@@ -175,8 +275,8 @@ def check(module, name, state, service_id, integration_key, api_key, incident_ke
|
||||
return incidents[0], False
|
||||
|
||||
|
||||
def send_event(module, service_key, event_type, desc,
|
||||
incident_key=None, client=None, client_url=None):
|
||||
def send_event_v1(module, service_key, event_type, desc,
|
||||
incident_key=None, client=None, client_url=None):
|
||||
url = "https://events.pagerduty.com/generic/2010-04-15/create_event.json"
|
||||
headers = {
|
||||
"Content-type": "application/json"
|
||||
@@ -200,61 +300,127 @@ def send_event(module, service_key, event_type, desc,
|
||||
return json_out
|
||||
|
||||
|
||||
def send_event_v2(module, service_key, event_type, payload, link,
|
||||
incident_key=None, client=None, client_url=None):
|
||||
url = "https://events.pagerduty.com/v2/enqueue"
|
||||
headers = {
|
||||
"Content-type": "application/json"
|
||||
}
|
||||
data = {
|
||||
"routing_key": service_key,
|
||||
"event_action": event_type,
|
||||
"payload": payload,
|
||||
"client": client,
|
||||
"client_url": client_url,
|
||||
}
|
||||
if link:
|
||||
data["links"] = [link]
|
||||
if incident_key:
|
||||
data["dedup_key"] = incident_key
|
||||
if event_type != "trigger":
|
||||
data.pop("payload")
|
||||
response, info = fetch_url(module, url, method="post",
|
||||
headers=headers, data=json.dumps(data))
|
||||
if info["status"] != 202:
|
||||
module.fail_json(msg="failed to %s. Reason: %s" %
|
||||
(event_type, info['msg']))
|
||||
json_out = json.loads(response.read())
|
||||
return json_out, True
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
name=dict(required=False),
|
||||
service_id=dict(required=True),
|
||||
service_key=dict(required=False, no_log=True),
|
||||
api_key=dict(required=False, no_log=True),
|
||||
integration_key=dict(required=False, no_log=True),
|
||||
api_key=dict(required=True, no_log=True),
|
||||
state=dict(required=True,
|
||||
choices=['triggered', 'acknowledged', 'resolved']),
|
||||
client=dict(required=False, default=None),
|
||||
client_url=dict(required=False, default=None),
|
||||
service_id=dict(required=False),
|
||||
service_key=dict(required=False, no_log=True),
|
||||
state=dict(
|
||||
required=True, choices=['triggered', 'acknowledged', 'resolved']
|
||||
),
|
||||
api_version=dict(type='str', default='v1', choices=['v1', 'v2']),
|
||||
client=dict(required=False),
|
||||
client_url=dict(required=False),
|
||||
component=dict(required=False),
|
||||
custom_details=dict(required=False, type='dict'),
|
||||
desc=dict(required=False, default='Created via Ansible'),
|
||||
incident_key=dict(required=False, default=None, no_log=False)
|
||||
incident_class=dict(required=False),
|
||||
incident_key=dict(required=False, no_log=False),
|
||||
link_url=dict(required=False),
|
||||
link_text=dict(required=False),
|
||||
source=dict(required=False),
|
||||
severity=dict(
|
||||
default='critical', choices=['critical', 'warning', 'error', 'info']
|
||||
),
|
||||
),
|
||||
supports_check_mode=True
|
||||
required_if=[
|
||||
('api_version', 'v1', ['service_id', 'api_key']),
|
||||
('state', 'acknowledged', ['incident_key']),
|
||||
('state', 'resolved', ['incident_key']),
|
||||
],
|
||||
required_one_of=[('service_key', 'integration_key')],
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
name = module.params['name']
|
||||
service_id = module.params['service_id']
|
||||
integration_key = module.params['integration_key']
|
||||
service_key = module.params['service_key']
|
||||
api_key = module.params['api_key']
|
||||
state = module.params['state']
|
||||
client = module.params['client']
|
||||
client_url = module.params['client_url']
|
||||
desc = module.params['desc']
|
||||
incident_key = module.params['incident_key']
|
||||
|
||||
service_id = module.params.get('service_id')
|
||||
integration_key = module.params.get('integration_key')
|
||||
service_key = module.params.get('service_key')
|
||||
api_key = module.params.get('api_key')
|
||||
state = module.params.get('state')
|
||||
client = module.params.get('client')
|
||||
client_url = module.params.get('client_url')
|
||||
desc = module.params.get('desc')
|
||||
incident_key = module.params.get('incident_key')
|
||||
payload = {
|
||||
'summary': desc,
|
||||
'source': module.params.get('source'),
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'severity': module.params.get('severity'),
|
||||
'component': module.params.get('component'),
|
||||
'class': module.params.get('incident_class'),
|
||||
'custom_details': module.params.get('custom_details'),
|
||||
}
|
||||
link = {}
|
||||
if module.params.get('link_url'):
|
||||
link['href'] = module.params.get('link_url')
|
||||
if module.params.get('link_text'):
|
||||
link['text'] = module.params.get('link_text')
|
||||
if integration_key is None:
|
||||
if service_key is not None:
|
||||
integration_key = service_key
|
||||
module.warn('"service_key" is obsolete parameter and will be removed.'
|
||||
' Please, use "integration_key" instead')
|
||||
else:
|
||||
module.fail_json(msg="'integration_key' is required parameter")
|
||||
integration_key = service_key
|
||||
module.warn(
|
||||
'"service_key" is obsolete parameter and will be removed.'
|
||||
' Please, use "integration_key" instead'
|
||||
)
|
||||
|
||||
state_event_dict = {
|
||||
'triggered': 'trigger',
|
||||
'acknowledged': 'acknowledge',
|
||||
'resolved': 'resolve'
|
||||
'resolved': 'resolve',
|
||||
}
|
||||
|
||||
event_type = state_event_dict[state]
|
||||
|
||||
if event_type != 'trigger' and incident_key is None:
|
||||
module.fail_json(msg="incident_key is required for "
|
||||
"acknowledge or resolve events")
|
||||
|
||||
out, changed = check(module, name, state, service_id,
|
||||
integration_key, api_key, incident_key)
|
||||
|
||||
if not module.check_mode and changed is True:
|
||||
out = send_event(module, integration_key, event_type, desc,
|
||||
incident_key, client, client_url)
|
||||
if module.params.get('api_version') == 'v1':
|
||||
out, changed = check(module, name, state, service_id,
|
||||
integration_key, api_key, incident_key)
|
||||
if not module.check_mode and changed is True:
|
||||
out = send_event_v1(module, integration_key, event_type, desc,
|
||||
incident_key, client, client_url)
|
||||
else:
|
||||
changed = True
|
||||
if event_type == 'trigger' and not payload['source']:
|
||||
module.fail_json(msg='"service" is a required variable for v2 api endpoint.')
|
||||
out, changed = send_event_v2(
|
||||
module,
|
||||
integration_key,
|
||||
event_type,
|
||||
payload,
|
||||
link,
|
||||
incident_key,
|
||||
client,
|
||||
client_url,
|
||||
)
|
||||
|
||||
module.exit_json(result=out, changed=changed)
|
||||
|
||||
|
||||
463
plugins/modules/pnpm.py
Normal file
463
plugins/modules/pnpm.py
Normal file
@@ -0,0 +1,463 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright (c) 2023 Aritra Sen <aretrosen@proton.me>
|
||||
# Copyright (c) 2017 Chris Hoffman <christopher.hoffman@gmail.com>
|
||||
# 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: pnpm
|
||||
short_description: Manage node.js packages with pnpm
|
||||
version_added: 7.4.0
|
||||
description:
|
||||
- Manage node.js packages with the L(pnpm package manager, https://pnpm.io/).
|
||||
author:
|
||||
- "Aritra Sen (@aretrosen)"
|
||||
- "Chris Hoffman (@chrishoffman), creator of NPM Ansible module"
|
||||
extends_documentation_fragment:
|
||||
- community.general.attributes
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: none
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of a node.js library to install.
|
||||
- All packages in package.json are installed if not provided.
|
||||
type: str
|
||||
required: false
|
||||
alias:
|
||||
description:
|
||||
- Alias of the node.js library.
|
||||
type: str
|
||||
required: false
|
||||
path:
|
||||
description:
|
||||
- The base path to install the node.js libraries.
|
||||
type: path
|
||||
required: false
|
||||
version:
|
||||
description:
|
||||
- The version of the library to be installed, in semver format.
|
||||
type: str
|
||||
required: false
|
||||
global:
|
||||
description:
|
||||
- Install the node.js library globally.
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
executable:
|
||||
description:
|
||||
- The executable location for pnpm.
|
||||
- The default location it searches for is E(PATH), fails if not set.
|
||||
type: path
|
||||
required: false
|
||||
ignore_scripts:
|
||||
description:
|
||||
- Use the C(--ignore-scripts) flag when installing.
|
||||
required: false
|
||||
type: bool
|
||||
default: false
|
||||
no_optional:
|
||||
description:
|
||||
- Do not install optional packages, equivalent to C(--no-optional).
|
||||
required: false
|
||||
type: bool
|
||||
default: false
|
||||
production:
|
||||
description:
|
||||
- Install dependencies in production mode.
|
||||
- Pnpm will ignore any dependencies under C(devDependencies) in package.json.
|
||||
required: false
|
||||
type: bool
|
||||
default: false
|
||||
dev:
|
||||
description:
|
||||
- Install dependencies in development mode.
|
||||
- Pnpm will ignore any regular dependencies in C(package.json).
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
optional:
|
||||
description:
|
||||
- Install dependencies in optional mode.
|
||||
required: false
|
||||
default: false
|
||||
type: bool
|
||||
state:
|
||||
description:
|
||||
- Installation state of the named node.js library.
|
||||
- If V(absent) is selected, a name option must be provided.
|
||||
type: str
|
||||
required: false
|
||||
default: present
|
||||
choices: ["present", "absent", "latest"]
|
||||
requirements:
|
||||
- Pnpm executable present in E(PATH).
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Install "tailwindcss" node.js package.
|
||||
community.general.pnpm:
|
||||
name: tailwindcss
|
||||
path: /app/location
|
||||
|
||||
- name: Install "tailwindcss" node.js package on version 3.3.2
|
||||
community.general.pnpm:
|
||||
name: tailwindcss
|
||||
version: 3.3.2
|
||||
path: /app/location
|
||||
|
||||
- name: Install "tailwindcss" node.js package globally.
|
||||
community.general.pnpm:
|
||||
name: tailwindcss
|
||||
global: true
|
||||
|
||||
- name: Install "tailwindcss" node.js package as dev dependency.
|
||||
community.general.pnpm:
|
||||
name: tailwindcss
|
||||
path: /app/location
|
||||
dev: true
|
||||
|
||||
- name: Install "tailwindcss" node.js package as optional dependency.
|
||||
community.general.pnpm:
|
||||
name: tailwindcss
|
||||
path: /app/location
|
||||
optional: true
|
||||
|
||||
- name: Install "tailwindcss" node.js package version 0.1.3 as tailwind-1
|
||||
community.general.pnpm:
|
||||
name: tailwindcss
|
||||
alias: tailwind-1
|
||||
version: 0.1.3
|
||||
path: /app/location
|
||||
|
||||
- name: Remove the globally-installed package "tailwindcss".
|
||||
community.general.pnpm:
|
||||
name: tailwindcss
|
||||
global: true
|
||||
state: absent
|
||||
|
||||
- name: Install packages based on package.json.
|
||||
community.general.pnpm:
|
||||
path: /app/location
|
||||
|
||||
- name: Update all packages in package.json to their latest version.
|
||||
community.general.pnpm:
|
||||
path: /app/location
|
||||
state: latest
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
|
||||
class Pnpm(object):
|
||||
def __init__(self, module, **kwargs):
|
||||
self.module = module
|
||||
self.name = kwargs["name"]
|
||||
self.alias = kwargs["alias"]
|
||||
self.version = kwargs["version"]
|
||||
self.path = kwargs["path"]
|
||||
self.globally = kwargs["globally"]
|
||||
self.executable = kwargs["executable"]
|
||||
self.ignore_scripts = kwargs["ignore_scripts"]
|
||||
self.no_optional = kwargs["no_optional"]
|
||||
self.production = kwargs["production"]
|
||||
self.dev = kwargs["dev"]
|
||||
self.optional = kwargs["optional"]
|
||||
|
||||
self.alias_name_ver = None
|
||||
|
||||
if self.alias is not None:
|
||||
self.alias_name_ver = self.alias + "@npm:"
|
||||
|
||||
if self.name is not None:
|
||||
self.alias_name_ver = (self.alias_name_ver or "") + self.name
|
||||
if self.version is not None:
|
||||
self.alias_name_ver = self.alias_name_ver + "@" + str(self.version)
|
||||
|
||||
def _exec(self, args, run_in_check_mode=False, check_rc=True):
|
||||
if not self.module.check_mode or (self.module.check_mode and run_in_check_mode):
|
||||
cmd = self.executable + args
|
||||
|
||||
if self.globally:
|
||||
cmd.append("-g")
|
||||
|
||||
if self.ignore_scripts:
|
||||
cmd.append("--ignore-scripts")
|
||||
|
||||
if self.no_optional:
|
||||
cmd.append("--no-optional")
|
||||
|
||||
if self.production:
|
||||
cmd.append("-P")
|
||||
|
||||
if self.dev:
|
||||
cmd.append("-D")
|
||||
|
||||
if self.name and self.optional:
|
||||
cmd.append("-O")
|
||||
|
||||
# If path is specified, cd into that path and run the command.
|
||||
cwd = None
|
||||
if self.path:
|
||||
if not os.path.exists(self.path):
|
||||
os.makedirs(self.path)
|
||||
|
||||
if not os.path.isdir(self.path):
|
||||
self.module.fail_json(msg="Path %s is not a directory" % self.path)
|
||||
|
||||
if not self.alias_name_ver and not os.path.isfile(
|
||||
os.path.join(self.path, "package.json")
|
||||
):
|
||||
self.module.fail_json(
|
||||
msg="package.json does not exist in provided path"
|
||||
)
|
||||
|
||||
cwd = self.path
|
||||
|
||||
_rc, out, err = self.module.run_command(cmd, check_rc=check_rc, cwd=cwd)
|
||||
return out, err
|
||||
|
||||
return None, None
|
||||
|
||||
def missing(self):
|
||||
if not os.path.isfile(os.path.join(self.path, "pnpm-lock.yaml")):
|
||||
return True
|
||||
|
||||
cmd = ["list", "--json"]
|
||||
|
||||
if self.name is not None:
|
||||
cmd.append(self.name)
|
||||
|
||||
try:
|
||||
out, err = self._exec(cmd, True, False)
|
||||
if err is not None and err != "":
|
||||
raise Exception(out)
|
||||
|
||||
data = json.loads(out)
|
||||
except Exception as e:
|
||||
self.module.fail_json(
|
||||
msg="Failed to parse pnpm output with error %s" % to_native(e)
|
||||
)
|
||||
|
||||
if "error" in data:
|
||||
return True
|
||||
|
||||
data = data[0]
|
||||
|
||||
for typedep in [
|
||||
"dependencies",
|
||||
"devDependencies",
|
||||
"optionalDependencies",
|
||||
"unsavedDependencies",
|
||||
]:
|
||||
if typedep not in data:
|
||||
continue
|
||||
|
||||
for dep, prop in data[typedep].items():
|
||||
if self.alias is not None and self.alias != dep:
|
||||
continue
|
||||
|
||||
name = prop["from"] if self.alias is not None else dep
|
||||
if self.name != name:
|
||||
continue
|
||||
|
||||
if self.version is None or self.version == prop["version"]:
|
||||
return False
|
||||
|
||||
break
|
||||
|
||||
return True
|
||||
|
||||
def install(self):
|
||||
if self.alias_name_ver is not None:
|
||||
return self._exec(["add", self.alias_name_ver])
|
||||
return self._exec(["install"])
|
||||
|
||||
def update(self):
|
||||
return self._exec(["update", "--latest"])
|
||||
|
||||
def uninstall(self):
|
||||
if self.alias is not None:
|
||||
return self._exec(["remove", self.alias])
|
||||
return self._exec(["remove", self.name])
|
||||
|
||||
def list_outdated(self):
|
||||
if not os.path.isfile(os.path.join(self.path, "pnpm-lock.yaml")):
|
||||
return list()
|
||||
|
||||
cmd = ["outdated", "--format", "json"]
|
||||
try:
|
||||
out, err = self._exec(cmd, True, False)
|
||||
|
||||
# BUG: It will not show correct error sometimes, like when it has
|
||||
# plain text output intermingled with a {}
|
||||
if err is not None and err != "":
|
||||
raise Exception(out)
|
||||
|
||||
# HACK: To fix the above bug, the following hack is implemented
|
||||
data_lines = out.splitlines(True)
|
||||
|
||||
out = None
|
||||
for line in data_lines:
|
||||
if len(line) > 0 and line[0] == "{":
|
||||
out = line
|
||||
continue
|
||||
|
||||
if len(line) > 0 and line[0] == "}":
|
||||
out += line
|
||||
break
|
||||
|
||||
if out is not None:
|
||||
out += line
|
||||
|
||||
data = json.loads(out)
|
||||
except Exception as e:
|
||||
self.module.fail_json(
|
||||
msg="Failed to parse pnpm output with error %s" % to_native(e)
|
||||
)
|
||||
|
||||
return data.keys()
|
||||
|
||||
|
||||
def main():
|
||||
arg_spec = dict(
|
||||
name=dict(default=None),
|
||||
alias=dict(default=None),
|
||||
path=dict(default=None, type="path"),
|
||||
version=dict(default=None),
|
||||
executable=dict(default=None, type="path"),
|
||||
ignore_scripts=dict(default=False, type="bool"),
|
||||
no_optional=dict(default=False, type="bool"),
|
||||
production=dict(default=False, type="bool"),
|
||||
dev=dict(default=False, type="bool"),
|
||||
optional=dict(default=False, type="bool"),
|
||||
state=dict(default="present", choices=["present", "absent", "latest"]),
|
||||
)
|
||||
arg_spec["global"] = dict(default=False, type="bool")
|
||||
module = AnsibleModule(argument_spec=arg_spec, supports_check_mode=True)
|
||||
|
||||
name = module.params["name"]
|
||||
alias = module.params["alias"]
|
||||
path = module.params["path"]
|
||||
version = module.params["version"]
|
||||
globally = module.params["global"]
|
||||
ignore_scripts = module.params["ignore_scripts"]
|
||||
no_optional = module.params["no_optional"]
|
||||
production = module.params["production"]
|
||||
dev = module.params["dev"]
|
||||
optional = module.params["optional"]
|
||||
state = module.params["state"]
|
||||
|
||||
if module.params["executable"]:
|
||||
executable = module.params["executable"].split(" ")
|
||||
else:
|
||||
executable = [module.get_bin_path("pnpm", True)]
|
||||
|
||||
if name is None and version is not None:
|
||||
module.fail_json(msg="version is meaningless when name is not provided")
|
||||
|
||||
if name is None and alias is not None:
|
||||
module.fail_json(msg="alias is meaningless when name is not provided")
|
||||
|
||||
if path is None and not globally:
|
||||
module.fail_json(msg="path must be specified when not using global")
|
||||
elif path is not None and globally:
|
||||
module.fail_json(msg="Cannot specify path when doing global installation")
|
||||
|
||||
if globally and (production or dev or optional):
|
||||
module.fail_json(
|
||||
msg="Options production, dev, and optional is meaningless when installing packages globally"
|
||||
)
|
||||
|
||||
if name is not None and path is not None and globally:
|
||||
module.fail_json(msg="path should not be mentioned when installing globally")
|
||||
|
||||
if production and dev and optional:
|
||||
module.fail_json(
|
||||
msg="Options production and dev and optional don't go together"
|
||||
)
|
||||
|
||||
if production and dev:
|
||||
module.fail_json(msg="Options production and dev don't go together")
|
||||
|
||||
if production and optional:
|
||||
module.fail_json(msg="Options production and optional don't go together")
|
||||
|
||||
if dev and optional:
|
||||
module.fail_json(msg="Options dev and optional don't go together")
|
||||
|
||||
if name is not None and name[0:4] == "http" and version is not None:
|
||||
module.fail_json(msg="Semver not supported on remote url downloads")
|
||||
|
||||
if name is None and optional:
|
||||
module.fail_json(
|
||||
msg="Optional not available when package name not provided, use no_optional instead"
|
||||
)
|
||||
|
||||
if state == "absent" and name is None:
|
||||
module.fail_json(msg="Package name is required for uninstalling")
|
||||
|
||||
if state == "latest":
|
||||
version = "latest"
|
||||
|
||||
if globally:
|
||||
_rc, out, _err = module.run_command(executable + ["root", "-g"], check_rc=True)
|
||||
path, _tail = os.path.split(out.strip())
|
||||
|
||||
pnpm = Pnpm(
|
||||
module,
|
||||
name=name,
|
||||
alias=alias,
|
||||
path=path,
|
||||
version=version,
|
||||
globally=globally,
|
||||
executable=executable,
|
||||
ignore_scripts=ignore_scripts,
|
||||
no_optional=no_optional,
|
||||
production=production,
|
||||
dev=dev,
|
||||
optional=optional,
|
||||
)
|
||||
|
||||
changed = False
|
||||
out = ""
|
||||
err = ""
|
||||
if state == "present":
|
||||
if pnpm.missing():
|
||||
changed = True
|
||||
out, err = pnpm.install()
|
||||
elif state == "latest":
|
||||
outdated = pnpm.list_outdated()
|
||||
if name is not None:
|
||||
if pnpm.missing() or name in outdated:
|
||||
changed = True
|
||||
out, err = pnpm.install()
|
||||
elif len(outdated):
|
||||
changed = True
|
||||
out, err = pnpm.update()
|
||||
else: # absent
|
||||
if not pnpm.missing():
|
||||
changed = True
|
||||
out, err = pnpm.uninstall()
|
||||
|
||||
module.exit_json(changed=changed, out=out, err=err)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -373,7 +373,7 @@ options:
|
||||
scsi:
|
||||
description:
|
||||
- A hash/dictionary of volume used as SCSI hard disk or CD-ROM. O(scsi='{"key":"value", "key":"value"}').
|
||||
- Keys allowed are - C(sata[n]) where 0 ≤ n ≤ 13.
|
||||
- Keys allowed are - C(scsi[n]) where 0 ≤ n ≤ 13.
|
||||
- Values allowed are - C("storage:size,format=value").
|
||||
- C(storage) is the storage identifier where to create the disk.
|
||||
- C(size) is the size of the disk in GB.
|
||||
@@ -415,6 +415,14 @@ options:
|
||||
smbios:
|
||||
description:
|
||||
- Specifies SMBIOS type 1 fields.
|
||||
- "Comma separated, Base64 encoded (optional) SMBIOS properties:"
|
||||
- V([base64=<1|0>] [,family=<Base64 encoded string>])
|
||||
- V([,manufacturer=<Base64 encoded string>])
|
||||
- V([,product=<Base64 encoded string>])
|
||||
- V([,serial=<Base64 encoded string>])
|
||||
- V([,sku=<Base64 encoded string>])
|
||||
- V([,uuid=<UUID>])
|
||||
- V([,version=<Base64 encoded string>])
|
||||
type: str
|
||||
snapname:
|
||||
description:
|
||||
@@ -524,7 +532,7 @@ options:
|
||||
virtio:
|
||||
description:
|
||||
- A hash/dictionary of volume used as VIRTIO hard disk. O(virtio='{"key":"value", "key":"value"}').
|
||||
- Keys allowed are - C(virto[n]) where 0 ≤ n ≤ 15.
|
||||
- Keys allowed are - C(virtio[n]) where 0 ≤ n ≤ 15.
|
||||
- Values allowed are - C("storage:size,format=value").
|
||||
- C(storage) is the storage identifier where to create the disk.
|
||||
- C(size) is the size of the disk in GB.
|
||||
|
||||
@@ -326,11 +326,13 @@ class Snap(StateModuleHelper):
|
||||
if x.startswith("warning: no snap found")]))
|
||||
return process_(rc, out, err)
|
||||
|
||||
with self.runner("info name", output_process=process) as ctx:
|
||||
try:
|
||||
names = ctx.run(name=snaps)
|
||||
finally:
|
||||
self.vars.snapinfo_run_info.append(ctx.run_info)
|
||||
names = []
|
||||
if snaps:
|
||||
with self.runner("info name", output_process=process) as ctx:
|
||||
try:
|
||||
names = ctx.run(name=snaps)
|
||||
finally:
|
||||
self.vars.snapinfo_run_info.append(ctx.run_info)
|
||||
return names
|
||||
|
||||
def snap_status(self, snap_name, channel):
|
||||
|
||||
@@ -21,6 +21,8 @@ from ansible_collections.community.general.plugins.module_utils.cmd_runner impor
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=dict(
|
||||
cmd=dict(type="str", default="echo"),
|
||||
path_prefix=dict(type="str"),
|
||||
arg_formats=dict(type="dict", default={}),
|
||||
arg_order=dict(type="raw", required=True),
|
||||
arg_values=dict(type="dict", default={}),
|
||||
@@ -41,7 +43,7 @@ def main():
|
||||
|
||||
arg_formats[arg] = func(*args)
|
||||
|
||||
runner = CmdRunner(module, ['echo', '--'], arg_formats=arg_formats)
|
||||
runner = CmdRunner(module, [module.params["cmd"], '--'], arg_formats=arg_formats, path_prefix=module.params["path_prefix"])
|
||||
|
||||
with runner.context(p['arg_order'], check_mode_skip=p['check_mode_skip']) as ctx:
|
||||
result = ctx.run(**p['arg_values'])
|
||||
|
||||
7
tests/integration/targets/cmd_runner/meta/main.yml
Normal file
7
tests/integration/targets/cmd_runner/meta/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
# 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
|
||||
|
||||
dependencies:
|
||||
- setup_remote_tmp_dir
|
||||
@@ -6,3 +6,4 @@
|
||||
ansible.builtin.include_tasks:
|
||||
file: test_cmd_echo.yml
|
||||
loop: "{{ cmd_echo_tests }}"
|
||||
when: item.condition | default(true) | bool
|
||||
|
||||
@@ -3,17 +3,26 @@
|
||||
# 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
|
||||
|
||||
- name: test cmd_echo [{{ item.name }}]
|
||||
cmd_echo:
|
||||
arg_formats: "{{ item.arg_formats|default(omit) }}"
|
||||
arg_order: "{{ item.arg_order }}"
|
||||
arg_values: "{{ item.arg_values|default(omit) }}"
|
||||
check_mode_skip: "{{ item.check_mode_skip|default(omit) }}"
|
||||
aa: "{{ item.aa|default(omit) }}"
|
||||
register: test_result
|
||||
check_mode: "{{ item.check_mode|default(omit) }}"
|
||||
ignore_errors: "{{ item.expect_error|default(omit) }}"
|
||||
- name: create copy of /bin/echo ({{ item.name }})
|
||||
ansible.builtin.copy:
|
||||
src: /bin/echo
|
||||
dest: "{{ item.copy_to }}/echo"
|
||||
mode: "755"
|
||||
when: item.copy_to is defined
|
||||
|
||||
- name: check results [{{ item.name }}]
|
||||
- name: test cmd_echo module ({{ item.name }})
|
||||
cmd_echo:
|
||||
cmd: "{{ item.cmd | default(omit) }}"
|
||||
path_prefix: "{{ item.path_prefix | default(omit) }}"
|
||||
arg_formats: "{{ item.arg_formats | default(omit) }}"
|
||||
arg_order: "{{ item.arg_order }}"
|
||||
arg_values: "{{ item.arg_values | default(omit) }}"
|
||||
check_mode_skip: "{{ item.check_mode_skip | default(omit) }}"
|
||||
aa: "{{ item.aa | default(omit) }}"
|
||||
register: test_result
|
||||
check_mode: "{{ item.check_mode | default(omit) }}"
|
||||
ignore_errors: "{{ item.expect_error | default(omit) }}"
|
||||
|
||||
- name: check results ({{ item.name }})
|
||||
assert:
|
||||
that: "{{ item.assertions }}"
|
||||
|
||||
@@ -138,3 +138,125 @@ cmd_echo_tests:
|
||||
- test_result.rc == 0
|
||||
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
|
||||
- test_result.err == ""
|
||||
|
||||
- name: use cmd echo
|
||||
cmd: echo
|
||||
arg_formats:
|
||||
aa:
|
||||
func: as_opt_eq_val
|
||||
args: [--answer]
|
||||
tt:
|
||||
func: as_opt_val
|
||||
args: [--tt-arg]
|
||||
arg_order: 'aa tt'
|
||||
arg_values:
|
||||
tt: potatoes
|
||||
aa: 11
|
||||
assertions:
|
||||
- test_result.rc == 0
|
||||
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
|
||||
- test_result.err == ""
|
||||
|
||||
- name: use cmd /bin/echo
|
||||
cmd: /bin/echo
|
||||
arg_formats:
|
||||
aa:
|
||||
func: as_opt_eq_val
|
||||
args: [--answer]
|
||||
tt:
|
||||
func: as_opt_val
|
||||
args: [--tt-arg]
|
||||
arg_order: 'aa tt'
|
||||
arg_values:
|
||||
tt: potatoes
|
||||
aa: 11
|
||||
assertions:
|
||||
- test_result.rc == 0
|
||||
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
|
||||
- test_result.err == ""
|
||||
|
||||
# this will not be in the regular set of paths get_bin_path() searches
|
||||
- name: use cmd {{ remote_tmp_dir }}/echo
|
||||
condition: >
|
||||
{{
|
||||
ansible_distribution != "MacOSX" and
|
||||
not (ansible_distribution == "CentOS" and ansible_distribution_major_version is version('7.0', '<'))
|
||||
}}
|
||||
copy_to: "{{ remote_tmp_dir }}"
|
||||
cmd: "{{ remote_tmp_dir }}/echo"
|
||||
arg_formats:
|
||||
aa:
|
||||
func: as_opt_eq_val
|
||||
args: [--answer]
|
||||
tt:
|
||||
func: as_opt_val
|
||||
args: [--tt-arg]
|
||||
arg_order: 'aa tt'
|
||||
arg_values:
|
||||
tt: potatoes
|
||||
aa: 11
|
||||
assertions:
|
||||
- test_result.rc == 0
|
||||
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
|
||||
- test_result.err == ""
|
||||
|
||||
- name: use cmd echo with path_prefix {{ remote_tmp_dir }}
|
||||
cmd: echo
|
||||
condition: >
|
||||
{{
|
||||
ansible_distribution != "MacOSX" and
|
||||
not (ansible_distribution == "CentOS" and ansible_distribution_major_version is version('7.0', '<'))
|
||||
}}
|
||||
copy_to: "{{ remote_tmp_dir }}"
|
||||
path_prefix: "{{ remote_tmp_dir }}"
|
||||
arg_formats:
|
||||
aa:
|
||||
func: as_opt_eq_val
|
||||
args: [--answer]
|
||||
tt:
|
||||
func: as_opt_val
|
||||
args: [--tt-arg]
|
||||
arg_order: 'aa tt'
|
||||
arg_values:
|
||||
tt: potatoes
|
||||
aa: 11
|
||||
assertions:
|
||||
- test_result.rc == 0
|
||||
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
|
||||
- test_result.err == ""
|
||||
|
||||
- name: use cmd never-existed
|
||||
cmd: never-existed
|
||||
arg_formats:
|
||||
aa:
|
||||
func: as_opt_eq_val
|
||||
args: [--answer]
|
||||
tt:
|
||||
func: as_opt_val
|
||||
args: [--tt-arg]
|
||||
arg_order: 'aa tt'
|
||||
arg_values:
|
||||
tt: potatoes
|
||||
aa: 11
|
||||
expect_error: true
|
||||
assertions:
|
||||
- >
|
||||
"Failed to find required executable" in test_result.msg
|
||||
|
||||
- name: use cmd /usr/bin/never-existed
|
||||
cmd: /usr/bin/never-existed
|
||||
arg_formats:
|
||||
aa:
|
||||
func: as_opt_eq_val
|
||||
args: [--answer]
|
||||
tt:
|
||||
func: as_opt_val
|
||||
args: [--tt-arg]
|
||||
arg_order: 'aa tt'
|
||||
arg_values:
|
||||
tt: potatoes
|
||||
aa: 11
|
||||
expect_error: true
|
||||
assertions:
|
||||
- >
|
||||
"No such file or directory" in test_result.msg
|
||||
|
||||
8
tests/integration/targets/pnpm/aliases
Normal file
8
tests/integration/targets/pnpm/aliases
Normal file
@@ -0,0 +1,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
|
||||
|
||||
azp/posix/2
|
||||
destructive
|
||||
skip/macos
|
||||
skip/freebsd
|
||||
9
tests/integration/targets/pnpm/meta/main.yml
Normal file
9
tests/integration/targets/pnpm/meta/main.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
---
|
||||
# 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
|
||||
|
||||
dependencies:
|
||||
- setup_pkg_mgr
|
||||
- setup_gnutar
|
||||
- setup_remote_tmp_dir
|
||||
26
tests/integration/targets/pnpm/tasks/main.yml
Normal file
26
tests/integration/targets/pnpm/tasks/main.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
####################################################################
|
||||
# WARNING: These are designed specifically for Ansible tests #
|
||||
# and should not be used as examples of how to write Ansible roles #
|
||||
####################################################################
|
||||
|
||||
# test code for the pnpm module
|
||||
# Copyright (c) 2023 Aritra Sen <aretrosen@proton.me>
|
||||
# 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
|
||||
|
||||
# -------------------------------------------------------------
|
||||
# Setup steps
|
||||
|
||||
- name: Run tests on OSes
|
||||
include_tasks: run.yml
|
||||
vars:
|
||||
ansible_system_os: "{{ ansible_system | lower }}"
|
||||
nodejs_version: "{{ item.node_version }}"
|
||||
nodejs_path: "node-v{{ nodejs_version }}-{{ ansible_system_os }}-x{{ ansible_userspace_bits }}"
|
||||
pnpm_version: "{{ item.pnpm_version }}"
|
||||
pnpm_path: "pnpm-{{ 'macos' if ansible_system_os == 'darwin' else 'linuxstatic' }}-x{{ ansible_userspace_bits }}"
|
||||
with_items:
|
||||
- { node_version: 16.20.0, pnpm_version: 8.7.0 }
|
||||
when:
|
||||
- not(ansible_distribution == 'Alpine') and not(ansible_distribution == 'CentOS' and ansible_distribution_major_version == '6')
|
||||
311
tests/integration/targets/pnpm/tasks/run.yml
Normal file
311
tests/integration/targets/pnpm/tasks/run.yml
Normal file
@@ -0,0 +1,311 @@
|
||||
---
|
||||
# Copyright (c) 2023 Aritra Sen <aretrosen@proton.me>
|
||||
# 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
|
||||
|
||||
- name: Download nodejs
|
||||
ansible.builtin.unarchive:
|
||||
src: "https://nodejs.org/dist/v{{ nodejs_version }}/{{ nodejs_path }}.tar.gz"
|
||||
dest: "{{ remote_tmp_dir }}"
|
||||
remote_src: true
|
||||
creates: "{{ remote_tmp_dir }}/{{ nodejs_path }}.tar.gz"
|
||||
|
||||
- name: Create a temporary directory for pnpm binary
|
||||
ansible.builtin.tempfile:
|
||||
state: directory
|
||||
register: tmp_dir
|
||||
|
||||
- name: Download pnpm binary to the temporary directory
|
||||
ansible.builtin.get_url:
|
||||
url: "https://github.com/pnpm/pnpm/releases/download/v{{ pnpm_version }}/{{ pnpm_path }}"
|
||||
dest: "{{ tmp_dir.path }}/pnpm"
|
||||
mode: "755"
|
||||
|
||||
- name: Setting up pnpm via command
|
||||
ansible.builtin.command: "{{ tmp_dir.path }}/pnpm setup --force"
|
||||
environment:
|
||||
PNPM_HOME: "{{ ansible_env.HOME }}/.local/share/pnpm"
|
||||
SHELL: /bin/sh
|
||||
ENV: "{{ ansible_env.HOME }}/.shrc"
|
||||
|
||||
- name: Remove the temporary directory
|
||||
ansible.builtin.file:
|
||||
path: "{{ tmp_dir.path }}"
|
||||
state: absent
|
||||
|
||||
- name: Remove any previous Nodejs modules
|
||||
ansible.builtin.file:
|
||||
path: "{{ remote_tmp_dir }}/node_modules"
|
||||
state: absent
|
||||
|
||||
- name: CI tests to run
|
||||
vars:
|
||||
node_bin_path: "{{ remote_tmp_dir }}/{{ nodejs_path }}/bin"
|
||||
pnpm_bin_path: "{{ ansible_env.HOME }}/.local/share/pnpm"
|
||||
package: "tailwindcss"
|
||||
environment:
|
||||
PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
|
||||
block:
|
||||
- name: Create dummy package.json
|
||||
ansible.builtin.template:
|
||||
src: package.j2
|
||||
dest: "{{ remote_tmp_dir }}/package.json"
|
||||
mode: "644"
|
||||
|
||||
- name: Install reading-time package via package.json
|
||||
pnpm:
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
executable: "{{ pnpm_bin_path }}/pnpm"
|
||||
state: present
|
||||
environment:
|
||||
PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
|
||||
- name: Install the same package from package.json again
|
||||
pnpm:
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
executable: "{{ pnpm_bin_path }}/pnpm"
|
||||
name: "reading-time"
|
||||
state: present
|
||||
environment:
|
||||
PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_install
|
||||
|
||||
- name: Assert that result is not changed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- not (pnpm_install is changed)
|
||||
|
||||
- name: Install all packages in check mode
|
||||
pnpm:
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
executable: "{{ pnpm_bin_path }}/pnpm"
|
||||
state: present
|
||||
environment:
|
||||
PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
check_mode: true
|
||||
register: pnpm_install_check
|
||||
|
||||
- name: Verify test pnpm global installation in check mode
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_install_check.err is defined
|
||||
- pnpm_install_check.out is defined
|
||||
- pnpm_install_check.err is none
|
||||
- pnpm_install_check.out is none
|
||||
|
||||
- name: Install package without dependency
|
||||
pnpm:
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
executable: "{{ pnpm_bin_path }}/pnpm"
|
||||
state: present
|
||||
name: "{{ package }}"
|
||||
environment:
|
||||
PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_install
|
||||
|
||||
- name: Assert that result is changed and successful
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_install is success
|
||||
- pnpm_install is changed
|
||||
|
||||
- name: Reinstall package without dependency
|
||||
pnpm:
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
executable: "{{ pnpm_bin_path }}/pnpm"
|
||||
state: present
|
||||
name: "{{ package }}"
|
||||
environment:
|
||||
PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_reinstall
|
||||
|
||||
- name: Assert that there is no change
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_reinstall is success
|
||||
- not (pnpm_reinstall is changed)
|
||||
|
||||
- name: Manually delete package
|
||||
ansible.builtin.file:
|
||||
path: "{{ remote_tmp_dir }}/node_modules/{{ package }}"
|
||||
state: absent
|
||||
|
||||
- name: Reinstall package
|
||||
pnpm:
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
executable: "{{ pnpm_bin_path }}/pnpm"
|
||||
state: latest
|
||||
name: "{{ package }}"
|
||||
environment:
|
||||
PATH: "{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_fix_install
|
||||
|
||||
- name: Assert that result is changed and successful
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_fix_install is success
|
||||
- pnpm_fix_install is changed
|
||||
|
||||
- name: Install package with version, without executable path
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
version: 0.1.3
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: present
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_install
|
||||
|
||||
- name: Assert that package with version is installed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_install is success
|
||||
- pnpm_install is changed
|
||||
|
||||
- name: Reinstall package with version, without explicit executable path
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
version: 0.1.3
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: present
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_reinstall
|
||||
|
||||
- name: Assert that there is no change
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_reinstall is success
|
||||
- not (pnpm_reinstall is changed)
|
||||
|
||||
- name: Update package, without executable path
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: latest
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_update
|
||||
|
||||
- name: Assert that result is changed and successful
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_update is success
|
||||
- pnpm_update is changed
|
||||
|
||||
- name: Remove package, without executable path
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: absent
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_absent
|
||||
|
||||
- name: Assert that result is changed and successful
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_absent is success
|
||||
- pnpm_absent is changed
|
||||
|
||||
- name: Install package with version and alias, without executable path
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
alias: tailwind-1
|
||||
version: 0.1.3
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: present
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_install
|
||||
|
||||
- name: Assert that package with version and alias is installed
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_install is success
|
||||
- pnpm_install is changed
|
||||
|
||||
- name: Reinstall package with version and alias, without explicit executable path
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
alias: tailwind-1
|
||||
version: 0.1.3
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: present
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_reinstall
|
||||
|
||||
- name: Assert that there is no change
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_reinstall is success
|
||||
- not (pnpm_reinstall is changed)
|
||||
|
||||
- name: Remove package with alias, without executable path
|
||||
pnpm:
|
||||
name: tailwindcss
|
||||
alias: tailwind-1
|
||||
path: "{{ remote_tmp_dir }}"
|
||||
state: absent
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
register: pnpm_absent
|
||||
|
||||
- name: Assert that result is changed and successful
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_absent is success
|
||||
- pnpm_absent is changed
|
||||
|
||||
- name: Install package without dependency globally
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
executable: "{{ pnpm_bin_path }}/pnpm"
|
||||
state: present
|
||||
global: true
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
PNPM_HOME: "{{ pnpm_bin_path }}"
|
||||
register: pnpm_install
|
||||
|
||||
- name: Assert that result is changed and successful
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_install is success
|
||||
- pnpm_install is changed
|
||||
|
||||
- name: Reinstall package globally, without explicit executable path
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
state: present
|
||||
global: true
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
PNPM_HOME: "{{ pnpm_bin_path }}"
|
||||
register: pnpm_reinstall
|
||||
|
||||
- name: Assert that there is no change
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_reinstall is success
|
||||
- not (pnpm_reinstall is changed)
|
||||
|
||||
- name: Remove package without dependency globally
|
||||
pnpm:
|
||||
name: "{{ package }}"
|
||||
executable: "{{ pnpm_bin_path }}/pnpm"
|
||||
global: true
|
||||
state: absent
|
||||
environment:
|
||||
PATH: "{{ pnpm_bin_path }}:{{ node_bin_path }}:{{ ansible_env.PATH }}"
|
||||
PNPM_HOME: "{{ pnpm_bin_path }}"
|
||||
register: pnpm_absent
|
||||
|
||||
- name: Assert that result is changed and successful
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- pnpm_absent is success
|
||||
- pnpm_absent is changed
|
||||
13
tests/integration/targets/pnpm/templates/package.j2
Normal file
13
tests/integration/targets/pnpm/templates/package.j2
Normal file
@@ -0,0 +1,13 @@
|
||||
{#
|
||||
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
|
||||
#}
|
||||
{
|
||||
"name": "ansible-pnpm-testing",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"reading-time": "^1.5.0"
|
||||
}
|
||||
}
|
||||
@@ -21,3 +21,8 @@
|
||||
update_cache: true
|
||||
upgrade: true
|
||||
when: archlinux_upgrade_tag is changed
|
||||
|
||||
- name: Remove EXTERNALLY-MANAGED file
|
||||
file:
|
||||
path: /usr/lib/python{{ ansible_python.version.major }}.{{ ansible_python.version.minor }}/EXTERNALLY-MANAGED
|
||||
state: absent
|
||||
|
||||
@@ -19,3 +19,5 @@
|
||||
ansible.builtin.include_tasks: test_dangerous.yml
|
||||
- name: Include test_3dash
|
||||
ansible.builtin.include_tasks: test_3dash.yml
|
||||
- name: Include test_empty_list
|
||||
ansible.builtin.include_tasks: test_empty_list.yml
|
||||
|
||||
14
tests/integration/targets/snap/tasks/test_empty_list.yml
Normal file
14
tests/integration/targets/snap/tasks/test_empty_list.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
# 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
|
||||
|
||||
- name: Empty list present
|
||||
community.general.snap:
|
||||
name: []
|
||||
state: present
|
||||
|
||||
- name: Empty list absent
|
||||
community.general.snap:
|
||||
name: []
|
||||
state: absent
|
||||
136
tests/unit/plugins/modules/cmd_runner_test_utils.py
Normal file
136
tests/unit/plugins/modules/cmd_runner_test_utils.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# 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
|
||||
|
||||
import json
|
||||
from collections import namedtuple
|
||||
from itertools import chain, repeat
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
|
||||
ModuleTestCase = namedtuple("ModuleTestCase", ["id", "input", "output", "run_command_calls", "flags"])
|
||||
RunCmdCall = namedtuple("RunCmdCall", ["command", "environ", "rc", "out", "err"])
|
||||
|
||||
|
||||
class CmdRunnerTestHelper(object):
|
||||
def __init__(self, module_main, test_cases):
|
||||
self.module_main = module_main
|
||||
self._test_cases = test_cases
|
||||
if isinstance(test_cases, (list, tuple)):
|
||||
self.testcases = test_cases
|
||||
else:
|
||||
self.testcases = self._make_test_cases()
|
||||
|
||||
@property
|
||||
def cmd_fixture(self):
|
||||
@pytest.fixture
|
||||
def patch_bin(mocker):
|
||||
def mockie(self, path, *args, **kwargs):
|
||||
return "/testbin/{0}".format(path)
|
||||
mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path', mockie)
|
||||
|
||||
return patch_bin
|
||||
|
||||
def _make_test_cases(self):
|
||||
test_cases = yaml.safe_load(self._test_cases)
|
||||
|
||||
results = []
|
||||
for tc in test_cases:
|
||||
for tc_param in ["input", "output", "flags"]:
|
||||
if not tc.get(tc_param):
|
||||
tc[tc_param] = {}
|
||||
if tc.get("run_command_calls"):
|
||||
tc["run_command_calls"] = [RunCmdCall(**r) for r in tc["run_command_calls"]]
|
||||
else:
|
||||
tc["run_command_calls"] = []
|
||||
results.append(ModuleTestCase(**tc))
|
||||
|
||||
return results
|
||||
|
||||
@property
|
||||
def testcases_params(self):
|
||||
return [[x.input, x] for x in self.testcases]
|
||||
|
||||
@property
|
||||
def testcases_ids(self):
|
||||
return [item.id for item in self.testcases]
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
return _Context(self, *args, **kwargs)
|
||||
|
||||
|
||||
class _Context(object):
|
||||
def __init__(self, helper, testcase, mocker, capfd):
|
||||
self.helper = helper
|
||||
self.testcase = testcase
|
||||
self.mocker = mocker
|
||||
self.capfd = capfd
|
||||
|
||||
self.run_cmd_calls = self.testcase.run_command_calls
|
||||
self.mock_run_cmd = self._make_mock_run_cmd()
|
||||
|
||||
def _make_mock_run_cmd(self):
|
||||
call_results = [(x.rc, x.out, x.err) for x in self.run_cmd_calls]
|
||||
error_call_results = (123,
|
||||
"OUT: testcase has not enough run_command calls",
|
||||
"ERR: testcase has not enough run_command calls")
|
||||
mock_run_command = self.mocker.patch('ansible.module_utils.basic.AnsibleModule.run_command',
|
||||
side_effect=chain(call_results, repeat(error_call_results)))
|
||||
return mock_run_command
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
return False
|
||||
|
||||
def _run(self):
|
||||
with pytest.raises(SystemExit):
|
||||
self.helper.module_main()
|
||||
|
||||
out, err = self.capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
|
||||
self.check_results(results)
|
||||
|
||||
def test_flags(self, flag=None):
|
||||
flags = self.testcase.flags
|
||||
if flag:
|
||||
flags = flags.get(flag)
|
||||
return flags
|
||||
|
||||
def run(self):
|
||||
func = self._run
|
||||
|
||||
test_flags = self.test_flags()
|
||||
if test_flags.get("skip"):
|
||||
pytest.skip(reason=test_flags["skip"])
|
||||
if test_flags.get("xfail"):
|
||||
pytest.xfail(reason=test_flags["xfail"])
|
||||
|
||||
func()
|
||||
|
||||
def check_results(self, results):
|
||||
print("testcase =\n%s" % str(self.testcase))
|
||||
print("results =\n%s" % results)
|
||||
if 'exception' in results:
|
||||
print("exception = \n%s" % results["exception"])
|
||||
|
||||
for test_result in self.testcase.output:
|
||||
assert results[test_result] == self.testcase.output[test_result], \
|
||||
"'{0}': '{1}' != '{2}'".format(test_result, results[test_result], self.testcase.output[test_result])
|
||||
|
||||
call_args_list = [(item[0][0], item[1]) for item in self.mock_run_cmd.call_args_list]
|
||||
expected_call_args_list = [(item.command, item.environ) for item in self.run_cmd_calls]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
|
||||
assert self.mock_run_cmd.call_count == len(self.run_cmd_calls)
|
||||
if self.mock_run_cmd.call_count:
|
||||
assert call_args_list == expected_call_args_list
|
||||
@@ -12,282 +12,26 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import cpanm
|
||||
|
||||
import pytest
|
||||
|
||||
TESTED_MODULE = cpanm.__name__
|
||||
from ansible_collections.community.general.plugins.modules import cpanm
|
||||
from .cmd_runner_test_utils import CmdRunnerTestHelper
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_cpanm(mocker):
|
||||
"""
|
||||
Function used for mocking some parts of redhat_subscription module
|
||||
"""
|
||||
mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path',
|
||||
return_value='/testbin/cpanm')
|
||||
|
||||
|
||||
TEST_CASES = [
|
||||
[
|
||||
{'name': 'Dancer'},
|
||||
{
|
||||
'id': 'install_dancer_compatibility',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/cpanm', '-le', 'use Dancer;'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
(2, '', 'error, not installed',), # output rc, out, err
|
||||
),
|
||||
(
|
||||
['/testbin/cpanm', 'Dancer'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
),
|
||||
],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer'},
|
||||
{
|
||||
'id': 'install_dancer_already_installed_compatibility',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/cpanm', '-le', 'use Dancer;'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
(0, '', '',), # output rc, out, err
|
||||
),
|
||||
],
|
||||
'changed': False,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_dancer',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', 'Dancer'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'MIYAGAWA/Plack-0.99_05.tar.gz'},
|
||||
{
|
||||
'id': 'install_distribution_file_compatibility',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', 'MIYAGAWA/Plack-0.99_05.tar.gz'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'MIYAGAWA/Plack-0.99_05.tar.gz', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_distribution_file',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', 'MIYAGAWA/Plack-0.99_05.tar.gz'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer', 'locallib': '/srv/webapps/my_app/extlib', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_into_locallib',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', '--local-lib', '/srv/webapps/my_app/extlib', 'Dancer'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'from_path': '/srv/webapps/my_app/src/', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_from_local_directory',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', '/srv/webapps/my_app/src/'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer', 'locallib': '/srv/webapps/my_app/extlib', 'notest': True, 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_into_locallib_no_unit_testing',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', '--notest', '--local-lib', '/srv/webapps/my_app/extlib', 'Dancer'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer', 'mirror': 'http://cpan.cpantesters.org/', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_from_mirror',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', '--mirror', 'http://cpan.cpantesters.org/', 'Dancer'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer', 'system_lib': True, 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_into_system_lib',
|
||||
'run_command.calls': [],
|
||||
'changed': False,
|
||||
'failed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer', 'version': '1.0', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_minversion_implicit',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', 'Dancer~1.0'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer', 'version': '~1.5', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_minversion_explicit',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', 'Dancer~1.5'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'Dancer', 'version': '@1.7', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_specific_version',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', 'Dancer@1.7'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
'failed': False,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'MIYAGAWA/Plack-0.99_05.tar.gz', 'version': '@1.7', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_specific_version_from_file_error',
|
||||
'run_command.calls': [],
|
||||
'changed': False,
|
||||
'failed': True,
|
||||
'msg': "parameter 'version' must not be used when installing from a file",
|
||||
}
|
||||
],
|
||||
[
|
||||
{'from_path': '~/', 'version': '@1.7', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_specific_version_from_directory_error',
|
||||
'run_command.calls': [],
|
||||
'changed': False,
|
||||
'failed': True,
|
||||
'msg': "parameter 'version' must not be used when installing from a directory",
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'git://github.com/plack/Plack.git', 'version': '@1.7', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_specific_version_from_git_url_explicit',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', 'git://github.com/plack/Plack.git@1.7'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
'failed': False,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'git://github.com/plack/Plack.git', 'version': '2.5', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_specific_version_from_git_url_implicit',
|
||||
'run_command.calls': [(
|
||||
['/testbin/cpanm', 'git://github.com/plack/Plack.git@2.5'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',), # output rc, out, err
|
||||
)],
|
||||
'changed': True,
|
||||
'failed': False,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'name': 'git://github.com/plack/Plack.git', 'version': '~2.5', 'mode': 'new'},
|
||||
{
|
||||
'id': 'install_version_operator_from_git_url_error',
|
||||
'run_command.calls': [],
|
||||
'changed': False,
|
||||
'failed': True,
|
||||
'msg': "operator '~' not allowed in version parameter when installing from git repository",
|
||||
}
|
||||
],
|
||||
]
|
||||
TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
|
||||
with open("tests/unit/plugins/modules/test_cpanm.yaml", "r") as TEST_CASES:
|
||||
helper = CmdRunnerTestHelper(cpanm.main, test_cases=TEST_CASES)
|
||||
patch_bin = helper.cmd_fixture
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module, testcase',
|
||||
TEST_CASES,
|
||||
ids=TEST_CASES_IDS,
|
||||
helper.testcases_params, ids=helper.testcases_ids,
|
||||
indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_cpanm(mocker, capfd, patch_cpanm, testcase):
|
||||
def test_module(mocker, capfd, patch_bin, testcase):
|
||||
"""
|
||||
Run unit tests for test cases listen in TEST_CASES
|
||||
Run unit tests for test cases listed in TEST_CASES
|
||||
"""
|
||||
|
||||
# Mock function used for running commands first
|
||||
call_results = [item[2] for item in testcase['run_command.calls']]
|
||||
mock_run_command = mocker.patch(
|
||||
'ansible_collections.community.general.plugins.module_utils.module_helper.AnsibleModule.run_command',
|
||||
side_effect=call_results)
|
||||
|
||||
# Try to run test case
|
||||
with pytest.raises(SystemExit):
|
||||
cpanm.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
print("results =\n%s" % results)
|
||||
|
||||
assert mock_run_command.call_count == len(testcase['run_command.calls'])
|
||||
if mock_run_command.call_count:
|
||||
call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
|
||||
expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
assert call_args_list == expected_call_args_list
|
||||
|
||||
assert results.get('changed', False) == testcase['changed']
|
||||
if 'failed' in testcase:
|
||||
assert results.get('failed', False) == testcase['failed']
|
||||
if 'msg' in testcase:
|
||||
assert results.get('msg', '') == testcase['msg']
|
||||
with helper(testcase, mocker, capfd) as testcase_context:
|
||||
testcase_context.run()
|
||||
|
||||
220
tests/unit/plugins/modules/test_cpanm.yaml
Normal file
220
tests/unit/plugins/modules/test_cpanm.yaml
Normal file
@@ -0,0 +1,220 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Alexei Znamensky (russoz@gmail.com)
|
||||
# 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
|
||||
|
||||
---
|
||||
- id: install_dancer_compatibility
|
||||
input:
|
||||
name: Dancer
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/perl, -le, 'use Dancer;']
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 2
|
||||
out: ""
|
||||
err: "error, not installed"
|
||||
- command: [/testbin/cpanm, Dancer]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_dancer_already_installed_compatibility
|
||||
input:
|
||||
name: Dancer
|
||||
output:
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/perl, -le, 'use Dancer;']
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_dancer
|
||||
input:
|
||||
name: Dancer
|
||||
mode: new
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, Dancer]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_distribution_file_compatibility
|
||||
input:
|
||||
name: MIYAGAWA/Plack-0.99_05.tar.gz
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, MIYAGAWA/Plack-0.99_05.tar.gz]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_distribution_file
|
||||
input:
|
||||
name: MIYAGAWA/Plack-0.99_05.tar.gz
|
||||
mode: new
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, MIYAGAWA/Plack-0.99_05.tar.gz]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_into_locallib
|
||||
input:
|
||||
name: Dancer
|
||||
mode: new
|
||||
locallib: /srv/webapps/my_app/extlib
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, --local-lib, /srv/webapps/my_app/extlib, Dancer]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_from_local_directory
|
||||
input:
|
||||
from_path: /srv/webapps/my_app/src/
|
||||
mode: new
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, /srv/webapps/my_app/src/]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_into_locallib_no_unit_testing
|
||||
input:
|
||||
name: Dancer
|
||||
notest: true
|
||||
mode: new
|
||||
locallib: /srv/webapps/my_app/extlib
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, --notest, --local-lib, /srv/webapps/my_app/extlib, Dancer]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_from_mirror
|
||||
input:
|
||||
name: Dancer
|
||||
mode: new
|
||||
mirror: "http://cpan.cpantesters.org/"
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, --mirror, "http://cpan.cpantesters.org/", Dancer]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_into_system_lib
|
||||
input:
|
||||
name: Dancer
|
||||
mode: new
|
||||
system_lib: true
|
||||
output:
|
||||
failed: true
|
||||
run_command_calls: []
|
||||
- id: install_minversion_implicit
|
||||
input:
|
||||
name: Dancer
|
||||
mode: new
|
||||
version: "1.0"
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, Dancer~1.0]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_minversion_explicit
|
||||
input:
|
||||
name: Dancer
|
||||
mode: new
|
||||
version: "~1.5"
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, Dancer~1.5]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_specific_version
|
||||
input:
|
||||
name: Dancer
|
||||
mode: new
|
||||
version: "@1.7"
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, Dancer@1.7]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_specific_version_from_file_error
|
||||
input:
|
||||
name: MIYAGAWA/Plack-0.99_05.tar.gz
|
||||
mode: new
|
||||
version: "@1.7"
|
||||
output:
|
||||
failed: true
|
||||
msg: parameter 'version' must not be used when installing from a file
|
||||
run_command_calls: []
|
||||
- id: install_specific_version_from_directory_error
|
||||
input:
|
||||
from_path: ~/
|
||||
mode: new
|
||||
version: "@1.7"
|
||||
output:
|
||||
failed: true
|
||||
msg: parameter 'version' must not be used when installing from a directory
|
||||
run_command_calls: []
|
||||
- id: install_specific_version_from_git_url_explicit
|
||||
input:
|
||||
name: "git://github.com/plack/Plack.git"
|
||||
mode: new
|
||||
version: "@1.7"
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, "git://github.com/plack/Plack.git@1.7"]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_specific_version_from_git_url_implicit
|
||||
input:
|
||||
name: "git://github.com/plack/Plack.git"
|
||||
mode: new
|
||||
version: "2.5"
|
||||
output:
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/cpanm, "git://github.com/plack/Plack.git@2.5"]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: install_version_operator_from_git_url_error
|
||||
input:
|
||||
name: "git://github.com/plack/Plack.git"
|
||||
mode: new
|
||||
version: "~2.5"
|
||||
output:
|
||||
failed: true
|
||||
msg: operator '~' not allowed in version parameter when installing from git repository
|
||||
run_command_calls: []
|
||||
@@ -6,208 +6,26 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import gconftool2
|
||||
|
||||
import pytest
|
||||
|
||||
TESTED_MODULE = gconftool2.__name__
|
||||
from ansible_collections.community.general.plugins.modules import gconftool2 as module
|
||||
from .cmd_runner_test_utils import CmdRunnerTestHelper
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_gconftool2(mocker):
|
||||
"""
|
||||
Function used for mocking some parts of redhat_subscription module
|
||||
"""
|
||||
mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path',
|
||||
return_value='/testbin/gconftool-2')
|
||||
|
||||
|
||||
TEST_CASES = [
|
||||
[
|
||||
{'state': 'get', 'key': '/desktop/gnome/background/picture_filename'},
|
||||
{
|
||||
'id': 'test_simple_element_get',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '100\n', '',),
|
||||
),
|
||||
],
|
||||
}
|
||||
],
|
||||
[
|
||||
{'state': 'get', 'key': '/desktop/gnome/background/picture_filename'},
|
||||
{
|
||||
'id': 'test_simple_element_get_not_found',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', "No value set for `/desktop/gnome/background/picture_filename'\n",),
|
||||
),
|
||||
],
|
||||
}
|
||||
],
|
||||
[
|
||||
{'state': 'present', 'key': '/desktop/gnome/background/picture_filename', 'value': 200, 'value_type': 'int'},
|
||||
{
|
||||
'id': 'test_simple_element_set',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '100\n', '',),
|
||||
),
|
||||
(
|
||||
['/testbin/gconftool-2', '--type', 'int', '--set', '/desktop/gnome/background/picture_filename', '200'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',),
|
||||
),
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '200\n', '',),
|
||||
),
|
||||
],
|
||||
'new_value': '200',
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'state': 'present', 'key': '/desktop/gnome/background/picture_filename', 'value': 200, 'value_type': 'int'},
|
||||
{
|
||||
'id': 'test_simple_element_set_idempotency_int',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '200\n', '',),
|
||||
),
|
||||
(
|
||||
['/testbin/gconftool-2', '--type', 'int', '--set', '/desktop/gnome/background/picture_filename', '200'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',),
|
||||
),
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '200\n', '',),
|
||||
),
|
||||
],
|
||||
'new_value': '200',
|
||||
'changed': False,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'state': 'present', 'key': '/apps/gnome_settings_daemon/screensaver/start_screensaver', 'value': 'false', 'value_type': 'bool'},
|
||||
{
|
||||
'id': 'test_simple_element_set_idempotency_bool',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/apps/gnome_settings_daemon/screensaver/start_screensaver'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, 'false\n', '',),
|
||||
),
|
||||
(
|
||||
['/testbin/gconftool-2', '--type', 'bool', '--set', '/apps/gnome_settings_daemon/screensaver/start_screensaver', 'false'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',),
|
||||
),
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/apps/gnome_settings_daemon/screensaver/start_screensaver'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, 'false\n', '',),
|
||||
),
|
||||
],
|
||||
'new_value': 'false',
|
||||
'changed': False,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'state': 'absent', 'key': '/desktop/gnome/background/picture_filename'},
|
||||
{
|
||||
'id': 'test_simple_element_unset',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '200\n', '',),
|
||||
),
|
||||
(
|
||||
['/testbin/gconftool-2', '--unset', '/desktop/gnome/background/picture_filename'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',),
|
||||
),
|
||||
],
|
||||
'changed': True,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'state': 'absent', 'key': '/apps/gnome_settings_daemon/screensaver/start_screensaver'},
|
||||
{
|
||||
'id': 'test_simple_element_unset_idempotency',
|
||||
'run_command.calls': [
|
||||
(
|
||||
['/testbin/gconftool-2', '--get', '/apps/gnome_settings_daemon/screensaver/start_screensaver'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',),
|
||||
),
|
||||
(
|
||||
['/testbin/gconftool-2', '--unset', '/apps/gnome_settings_daemon/screensaver/start_screensaver'],
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
(0, '', '',),
|
||||
),
|
||||
],
|
||||
'changed': False,
|
||||
}
|
||||
],
|
||||
]
|
||||
TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
|
||||
with open("tests/unit/plugins/modules/test_gconftool2.yaml", "r") as TEST_CASES:
|
||||
helper = CmdRunnerTestHelper(module.main, test_cases=TEST_CASES)
|
||||
patch_bin = helper.cmd_fixture
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module, testcase',
|
||||
TEST_CASES,
|
||||
ids=TEST_CASES_IDS,
|
||||
helper.testcases_params, ids=helper.testcases_ids,
|
||||
indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_gconftool2(mocker, capfd, patch_gconftool2, testcase):
|
||||
def test_module(mocker, capfd, patch_bin, testcase):
|
||||
"""
|
||||
Run unit tests for test cases listen in TEST_CASES
|
||||
Run unit tests for test cases listed in TEST_CASES
|
||||
"""
|
||||
|
||||
# Mock function used for running commands first
|
||||
call_results = [item[2] for item in testcase['run_command.calls']]
|
||||
mock_run_command = mocker.patch(
|
||||
'ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.run_command',
|
||||
side_effect=call_results)
|
||||
|
||||
# Try to run test case
|
||||
with pytest.raises(SystemExit):
|
||||
gconftool2.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
print("testcase =\n%s" % testcase)
|
||||
print("results =\n%s" % results)
|
||||
|
||||
if 'changed' in testcase:
|
||||
assert results.get('changed', False) == testcase['changed']
|
||||
if 'new_value' in testcase:
|
||||
assert results.get('new_value', None) == testcase['new_value']
|
||||
|
||||
for conditional_test_result in ('value',):
|
||||
if conditional_test_result in testcase:
|
||||
assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results)
|
||||
assert results[conditional_test_result] == testcase[conditional_test_result], \
|
||||
"'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result])
|
||||
|
||||
assert mock_run_command.call_count == len(testcase['run_command.calls'])
|
||||
if mock_run_command.call_count:
|
||||
call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
|
||||
expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
assert call_args_list == expected_call_args_list
|
||||
with helper(testcase, mocker, capfd) as testcase_context:
|
||||
testcase_context.run()
|
||||
|
||||
139
tests/unit/plugins/modules/test_gconftool2.yaml
Normal file
139
tests/unit/plugins/modules/test_gconftool2.yaml
Normal file
@@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Alexei Znamensky (russoz@gmail.com)
|
||||
# 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
|
||||
|
||||
---
|
||||
- id: test_simple_element_get
|
||||
input:
|
||||
state: get
|
||||
key: /desktop/gnome/background/picture_filename
|
||||
output: {}
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "100\n"
|
||||
err: ""
|
||||
- id: test_simple_element_get_not_found
|
||||
input:
|
||||
state: get
|
||||
key: /desktop/gnome/background/picture_filename
|
||||
output: {}
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: "No value set for `/desktop/gnome/background/picture_filename'\n"
|
||||
- id: test_simple_element_set
|
||||
input:
|
||||
state: present
|
||||
key: /desktop/gnome/background/picture_filename
|
||||
value: 200
|
||||
value_type: int
|
||||
output:
|
||||
new_value: '200'
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "100\n"
|
||||
err: ""
|
||||
- command: [/testbin/gconftool-2, --type, int, --set, /desktop/gnome/background/picture_filename, "200"]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "200\n"
|
||||
err: ""
|
||||
- id: test_simple_element_set_idempotency_int
|
||||
input:
|
||||
state: present
|
||||
key: /desktop/gnome/background/picture_filename
|
||||
value: 200
|
||||
value_type: int
|
||||
output:
|
||||
new_value: '200'
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "200\n"
|
||||
err: ""
|
||||
- command: [/testbin/gconftool-2, --type, int, --set, /desktop/gnome/background/picture_filename, "200"]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "200\n"
|
||||
err: ""
|
||||
- id: test_simple_element_set_idempotency_bool
|
||||
input:
|
||||
state: present
|
||||
key: /apps/gnome_settings_daemon/screensaver/start_screensaver
|
||||
value: false
|
||||
value_type: bool
|
||||
output:
|
||||
new_value: 'false'
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /apps/gnome_settings_daemon/screensaver/start_screensaver]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "false\n"
|
||||
err: ""
|
||||
- command: [/testbin/gconftool-2, --type, bool, --set, /apps/gnome_settings_daemon/screensaver/start_screensaver, "False"]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- command: [/testbin/gconftool-2, --get, /apps/gnome_settings_daemon/screensaver/start_screensaver]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "false\n"
|
||||
err: ""
|
||||
- id: test_simple_element_unset
|
||||
input:
|
||||
state: absent
|
||||
key: /desktop/gnome/background/picture_filename
|
||||
output:
|
||||
new_value: null
|
||||
changed: true
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "200\n"
|
||||
err: ""
|
||||
- command: [/testbin/gconftool-2, --unset, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: test_simple_element_unset_idempotency
|
||||
input:
|
||||
state: absent
|
||||
key: /apps/gnome_settings_daemon/screensaver/start_screensaver
|
||||
output:
|
||||
new_value: null
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /apps/gnome_settings_daemon/screensaver/start_screensaver]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- command: [/testbin/gconftool-2, --unset, /apps/gnome_settings_daemon/screensaver/start_screensaver]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
@@ -6,98 +6,26 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import gconftool2_info
|
||||
|
||||
import pytest
|
||||
|
||||
TESTED_MODULE = gconftool2_info.__name__
|
||||
from ansible_collections.community.general.plugins.modules import gconftool2_info
|
||||
from .cmd_runner_test_utils import CmdRunnerTestHelper
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_gconftool2_info(mocker):
|
||||
"""
|
||||
Function used for mocking some parts of redhat_subscription module
|
||||
"""
|
||||
mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path',
|
||||
return_value='/testbin/gconftool-2')
|
||||
|
||||
|
||||
TEST_CASES = [
|
||||
[
|
||||
{'key': '/desktop/gnome/background/picture_filename'},
|
||||
{
|
||||
'id': 'test_simple_element_get',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '100\n', '',),
|
||||
),
|
||||
],
|
||||
'value': '100',
|
||||
}
|
||||
],
|
||||
[
|
||||
{'key': '/desktop/gnome/background/picture_filename'},
|
||||
{
|
||||
'id': 'test_simple_element_get_not_found',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/gconftool-2', '--get', '/desktop/gnome/background/picture_filename'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '', "No value set for `/desktop/gnome/background/picture_filename'\n",),
|
||||
),
|
||||
],
|
||||
'value': None,
|
||||
}
|
||||
],
|
||||
]
|
||||
TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
|
||||
with open("tests/unit/plugins/modules/test_gconftool2_info.yaml", "r") as TEST_CASES:
|
||||
helper = CmdRunnerTestHelper(gconftool2_info.main, test_cases=TEST_CASES)
|
||||
patch_bin = helper.cmd_fixture
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module, testcase',
|
||||
TEST_CASES,
|
||||
ids=TEST_CASES_IDS,
|
||||
helper.testcases_params, ids=helper.testcases_ids,
|
||||
indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_gconftool2_info(mocker, capfd, patch_gconftool2_info, testcase):
|
||||
def test_module(mocker, capfd, patch_bin, testcase):
|
||||
"""
|
||||
Run unit tests for test cases listen in TEST_CASES
|
||||
Run unit tests for test cases listed in TEST_CASES
|
||||
"""
|
||||
|
||||
# Mock function used for running commands first
|
||||
call_results = [item[2] for item in testcase['run_command.calls']]
|
||||
mock_run_command = mocker.patch(
|
||||
'ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.run_command',
|
||||
side_effect=call_results)
|
||||
|
||||
# Try to run test case
|
||||
with pytest.raises(SystemExit):
|
||||
gconftool2_info.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
print("testcase =\n%s" % testcase)
|
||||
print("results =\n%s" % results)
|
||||
|
||||
for conditional_test_result in ('value',):
|
||||
if conditional_test_result in testcase:
|
||||
assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results)
|
||||
assert results[conditional_test_result] == testcase[conditional_test_result], \
|
||||
"'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result])
|
||||
|
||||
assert mock_run_command.call_count == len(testcase['run_command.calls'])
|
||||
if mock_run_command.call_count:
|
||||
call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
|
||||
expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
assert call_args_list == expected_call_args_list
|
||||
with helper(testcase, mocker, capfd) as testcase_context:
|
||||
testcase_context.run()
|
||||
|
||||
28
tests/unit/plugins/modules/test_gconftool2_info.yaml
Normal file
28
tests/unit/plugins/modules/test_gconftool2_info.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Alexei Znamensky (russoz@gmail.com)
|
||||
# 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
|
||||
|
||||
---
|
||||
- id: test_simple_element_get
|
||||
input:
|
||||
key: /desktop/gnome/background/picture_filename
|
||||
output:
|
||||
value: '100'
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "100\n"
|
||||
err: ""
|
||||
- id: test_simple_element_get_not_found
|
||||
input:
|
||||
key: /desktop/gnome/background/picture_filename
|
||||
output:
|
||||
value: null
|
||||
run_command_calls:
|
||||
- command: [/testbin/gconftool-2, --get, /desktop/gnome/background/picture_filename]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: "No value set for `/desktop/gnome/background/picture_filename'\n"
|
||||
@@ -75,6 +75,11 @@ class JenkinsMock():
|
||||
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"
|
||||
@@ -222,3 +227,38 @@ class TestJenkinsBuild(unittest.TestCase):
|
||||
"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'])
|
||||
|
||||
180
tests/unit/plugins/modules/test_jenkins_build_info.py
Normal file
180
tests/unit/plugins/modules/test_jenkins_build_info.py
Normal file
@@ -0,0 +1,180 @@
|
||||
# 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
|
||||
|
||||
from ansible_collections.community.general.tests.unit.compat import unittest
|
||||
from ansible_collections.community.general.tests.unit.compat.mock import patch
|
||||
from ansible.module_utils import basic
|
||||
from ansible.module_utils.common.text.converters import to_bytes
|
||||
from ansible_collections.community.general.plugins.modules import jenkins_build_info
|
||||
|
||||
import json
|
||||
|
||||
|
||||
def set_module_args(args):
|
||||
"""prepare arguments so that they will be picked up during module creation"""
|
||||
args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
|
||||
basic._ANSIBLE_ARGS = to_bytes(args)
|
||||
|
||||
|
||||
class AnsibleExitJson(Exception):
|
||||
"""Exception class to be raised by module.exit_json and caught by the test case"""
|
||||
pass
|
||||
|
||||
|
||||
class AnsibleFailJson(Exception):
|
||||
"""Exception class to be raised by module.fail_json and caught by the test case"""
|
||||
pass
|
||||
|
||||
|
||||
def exit_json(*args, **kwargs):
|
||||
"""function to patch over exit_json; package return data into an exception"""
|
||||
if 'changed' not in kwargs:
|
||||
kwargs['changed'] = False
|
||||
raise AnsibleExitJson(kwargs)
|
||||
|
||||
|
||||
def fail_json(*args, **kwargs):
|
||||
"""function to patch over fail_json; package return data into an exception"""
|
||||
kwargs['failed'] = True
|
||||
raise AnsibleFailJson(kwargs)
|
||||
|
||||
|
||||
class jenkins:
|
||||
class JenkinsException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class JenkinsBuildMock():
|
||||
def __init__(self, name, build_number=None):
|
||||
self.name = name
|
||||
self.build_number = build_number
|
||||
|
||||
def get_build_status(self):
|
||||
try:
|
||||
instance = JenkinsMock()
|
||||
response = JenkinsMock.get_build_info(instance, self.name, self.build_number)
|
||||
return response
|
||||
except jenkins.JenkinsException:
|
||||
response = {}
|
||||
response["result"] = "ABSENT"
|
||||
return response
|
||||
except Exception as e:
|
||||
fail_json(msg='Unable to fetch build information, {0}'.format(e))
|
||||
|
||||
|
||||
class JenkinsMock():
|
||||
|
||||
def get_build_info(self, name, build_number):
|
||||
if name == "job-absent":
|
||||
raise jenkins.JenkinsException()
|
||||
|
||||
return {
|
||||
"result": "SUCCESS",
|
||||
"build_info": {}
|
||||
}
|
||||
|
||||
def get_job_info(self, name):
|
||||
if name == "job-absent":
|
||||
raise jenkins.JenkinsException()
|
||||
|
||||
return {
|
||||
"lastBuild": {
|
||||
"number": 123
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TestJenkinsBuildInfo(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.mock_module_helper = patch.multiple(basic.AnsibleModule,
|
||||
exit_json=exit_json,
|
||||
fail_json=fail_json)
|
||||
self.mock_module_helper.start()
|
||||
self.addCleanup(self.mock_module_helper.stop)
|
||||
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
|
||||
def test_module_fail_when_required_args_missing(self, test_deps):
|
||||
test_deps.return_value = None
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({})
|
||||
jenkins_build_info.main()
|
||||
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_jenkins_connection')
|
||||
def test_module_get_build_info(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": "job-present",
|
||||
"user": "abc",
|
||||
"token": "xyz",
|
||||
"build_number": 30
|
||||
})
|
||||
jenkins_build_info.main()
|
||||
|
||||
self.assertFalse(return_json.exception.args[0]["changed"])
|
||||
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_jenkins_connection')
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_build_status')
|
||||
def test_module_get_build_info_if_build_does_not_exist(self, build_status, jenkins_connection, test_deps):
|
||||
test_deps.return_value = None
|
||||
jenkins_connection.return_value = JenkinsMock()
|
||||
build_status.return_value = JenkinsBuildMock("job-absent", 30).get_build_status()
|
||||
|
||||
with self.assertRaises(AnsibleExitJson) as return_json:
|
||||
set_module_args({
|
||||
"name": "job-absent",
|
||||
"user": "abc",
|
||||
"token": "xyz",
|
||||
"build_number": 30
|
||||
})
|
||||
jenkins_build_info.main()
|
||||
|
||||
self.assertFalse(return_json.exception.args[0]['changed'])
|
||||
self.assertTrue(return_json.exception.args[0]['failed'])
|
||||
self.assertEquals("ABSENT", return_json.exception.args[0]['build_info']['result'])
|
||||
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_jenkins_connection')
|
||||
def test_module_get_build_info_get_last_build(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": "job-present",
|
||||
"user": "abc",
|
||||
"token": "xyz"
|
||||
})
|
||||
jenkins_build_info.main()
|
||||
|
||||
self.assertFalse(return_json.exception.args[0]['changed'])
|
||||
self.assertEquals("SUCCESS", return_json.exception.args[0]['build_info']['result'])
|
||||
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.test_dependencies')
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_jenkins_connection')
|
||||
@patch('ansible_collections.community.general.plugins.modules.jenkins_build_info.JenkinsBuildInfo.get_build_status')
|
||||
def test_module_get_build_info_if_job_does_not_exist(self, build_status, jenkins_connection, test_deps):
|
||||
test_deps.return_value = None
|
||||
jenkins_connection.return_value = JenkinsMock()
|
||||
build_status.return_value = JenkinsBuildMock("job-absent").get_build_status()
|
||||
|
||||
with self.assertRaises(AnsibleExitJson) as return_json:
|
||||
set_module_args({
|
||||
"name": "job-absent",
|
||||
"user": "abc",
|
||||
"token": "xyz"
|
||||
})
|
||||
jenkins_build_info.main()
|
||||
|
||||
self.assertFalse(return_json.exception.args[0]['changed'])
|
||||
self.assertTrue(return_json.exception.args[0]['failed'])
|
||||
self.assertEquals("ABSENT", return_json.exception.args[0]['build_info']['result'])
|
||||
@@ -6,236 +6,26 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from collections import namedtuple
|
||||
from ansible_collections.community.general.plugins.modules import opkg
|
||||
|
||||
import pytest
|
||||
|
||||
TESTED_MODULE = opkg.__name__
|
||||
from ansible_collections.community.general.plugins.modules import opkg as module
|
||||
from .cmd_runner_test_utils import CmdRunnerTestHelper
|
||||
|
||||
|
||||
ModuleTestCase = namedtuple("ModuleTestCase", ["id", "input", "output", "run_command_calls"])
|
||||
RunCmdCall = namedtuple("RunCmdCall", ["command", "environ", "rc", "out", "err"])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_opkg(mocker):
|
||||
mocker.patch('ansible.module_utils.basic.AnsibleModule.get_bin_path', return_value='/testbin/opkg')
|
||||
|
||||
|
||||
TEST_CASES = [
|
||||
ModuleTestCase(
|
||||
id="install_zlibdev",
|
||||
input={"name": "zlib-dev", "state": "present"},
|
||||
output={
|
||||
"msg": "installed 1 package(s)"
|
||||
},
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "install", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out=(
|
||||
"Installing zlib-dev (1.2.11-6) to root..."
|
||||
"Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk"
|
||||
"Installing zlib (1.2.11-6) to root..."
|
||||
"Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk"
|
||||
"Configuring zlib."
|
||||
"Configuring zlib-dev."
|
||||
),
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="zlib-dev - 1.2.11-6\n",
|
||||
err="",
|
||||
),
|
||||
],
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="install_zlibdev_present",
|
||||
input={"name": "zlib-dev", "state": "present"},
|
||||
output={
|
||||
"msg": "package(s) already present"
|
||||
},
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="zlib-dev - 1.2.11-6\n",
|
||||
err="",
|
||||
),
|
||||
],
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="install_zlibdev_force_reinstall",
|
||||
input={"name": "zlib-dev", "state": "present", "force": "reinstall"},
|
||||
output={
|
||||
"msg": "installed 1 package(s)"
|
||||
},
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="zlib-dev - 1.2.11-6\n",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "install", "--force-reinstall", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out=(
|
||||
"Installing zlib-dev (1.2.11-6) to root...\n"
|
||||
"Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk\n"
|
||||
"Configuring zlib-dev.\n"
|
||||
),
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="zlib-dev - 1.2.11-6\n",
|
||||
err="",
|
||||
),
|
||||
],
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="install_zlibdev_with_version",
|
||||
input={"name": "zlib-dev=1.2.11-6", "state": "present"},
|
||||
output={
|
||||
"msg": "installed 1 package(s)"
|
||||
},
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "install", "zlib-dev=1.2.11-6"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out=(
|
||||
"Installing zlib-dev (1.2.11-6) to root..."
|
||||
"Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk"
|
||||
"Installing zlib (1.2.11-6) to root..."
|
||||
"Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk"
|
||||
"Configuring zlib."
|
||||
"Configuring zlib-dev."
|
||||
),
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "zlib-dev"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="zlib-dev - 1.2.11-6 \n", # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
|
||||
err="",
|
||||
),
|
||||
],
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="install_vim_updatecache",
|
||||
input={"name": "vim-fuller", "state": "present", "update_cache": True},
|
||||
output={
|
||||
"msg": "installed 1 package(s)"
|
||||
},
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "update"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "vim-fuller"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "install", "vim-fuller"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out=(
|
||||
"Multiple packages (libgcc1 and libgcc1) providing same name marked HOLD or PREFER. Using latest.\n"
|
||||
"Installing vim-fuller (9.0-1) to root...\n"
|
||||
"Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/packages/vim-fuller_9.0-1_x86_64.ipk\n"
|
||||
"Installing terminfo (6.4-2) to root...\n"
|
||||
"Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/terminfo_6.4-2_x86_64.ipk\n"
|
||||
"Installing libncurses6 (6.4-2) to root...\n"
|
||||
"Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/libncurses6_6.4-2_x86_64.ipk\n"
|
||||
"Configuring terminfo.\n"
|
||||
"Configuring libncurses6.\n"
|
||||
"Configuring vim-fuller.\n"
|
||||
),
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=["/testbin/opkg", "list-installed", "vim-fuller"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="vim-fuller - 9.0-1 \n", # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
|
||||
err="",
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
TEST_CASES_IDS = [item.id for item in TEST_CASES]
|
||||
with open("tests/unit/plugins/modules/test_opkg.yaml", "r") as TEST_CASES:
|
||||
helper = CmdRunnerTestHelper(module.main, test_cases=TEST_CASES)
|
||||
patch_bin = helper.cmd_fixture
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module, testcase',
|
||||
[[x.input, x] for x in TEST_CASES],
|
||||
ids=TEST_CASES_IDS,
|
||||
helper.testcases_params, ids=helper.testcases_ids,
|
||||
indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_opkg(mocker, capfd, patch_opkg, testcase):
|
||||
def test_module(mocker, capfd, patch_bin, testcase):
|
||||
"""
|
||||
Run unit tests for test cases listen in TEST_CASES
|
||||
Run unit tests for test cases listed in TEST_CASES
|
||||
"""
|
||||
|
||||
run_cmd_calls = testcase.run_command_calls
|
||||
|
||||
# Mock function used for running commands first
|
||||
call_results = [(x.rc, x.out, x.err) for x in run_cmd_calls]
|
||||
mock_run_command = mocker.patch('ansible.module_utils.basic.AnsibleModule.run_command', side_effect=call_results)
|
||||
|
||||
# Try to run test case
|
||||
with pytest.raises(SystemExit):
|
||||
opkg.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
print("testcase =\n%s" % str(testcase))
|
||||
print("results =\n%s" % results)
|
||||
|
||||
for test_result in testcase.output:
|
||||
assert results[test_result] == testcase.output[test_result], \
|
||||
"'{0}': '{1}' != '{2}'".format(test_result, results[test_result], testcase.output[test_result])
|
||||
|
||||
call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
|
||||
expected_call_args_list = [(item.command, item.environ) for item in run_cmd_calls]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
|
||||
assert mock_run_command.call_count == len(run_cmd_calls)
|
||||
if mock_run_command.call_count:
|
||||
assert call_args_list == expected_call_args_list
|
||||
with helper(testcase, mocker, capfd) as testcase_context:
|
||||
testcase_context.run()
|
||||
|
||||
142
tests/unit/plugins/modules/test_opkg.yaml
Normal file
142
tests/unit/plugins/modules/test_opkg.yaml
Normal file
@@ -0,0 +1,142 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Alexei Znamensky (russoz@gmail.com)
|
||||
# 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
|
||||
|
||||
---
|
||||
- id: install_zlibdev
|
||||
input:
|
||||
name: zlib-dev
|
||||
state: present
|
||||
output:
|
||||
msg: installed 1 package(s)
|
||||
run_command_calls:
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- command: [/testbin/opkg, install, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: |
|
||||
Installing zlib-dev (1.2.11-6) to root...
|
||||
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk
|
||||
Installing zlib (1.2.11-6) to root...
|
||||
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk
|
||||
Configuring zlib.
|
||||
Configuring zlib-dev.
|
||||
err: ""
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: |
|
||||
zlib-dev - 1.2.11-6
|
||||
err: ""
|
||||
- id: install_zlibdev_present
|
||||
input:
|
||||
name: zlib-dev
|
||||
state: present
|
||||
output:
|
||||
msg: package(s) already present
|
||||
run_command_calls:
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: |
|
||||
zlib-dev - 1.2.11-6
|
||||
err: ""
|
||||
- id: install_zlibdev_force_reinstall
|
||||
input:
|
||||
name: zlib-dev
|
||||
state: present
|
||||
force: reinstall
|
||||
output:
|
||||
msg: installed 1 package(s)
|
||||
run_command_calls:
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: |
|
||||
zlib-dev - 1.2.11-6
|
||||
err: ""
|
||||
- command: [/testbin/opkg, install, --force-reinstall, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: |
|
||||
Installing zlib-dev (1.2.11-6) to root...
|
||||
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk
|
||||
Configuring zlib-dev.
|
||||
err: ""
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: |
|
||||
zlib-dev - 1.2.11-6
|
||||
err: ""
|
||||
- id: install_zlibdev_with_version
|
||||
input:
|
||||
name: zlib-dev=1.2.11-6
|
||||
state: present
|
||||
output:
|
||||
msg: installed 1 package(s)
|
||||
run_command_calls:
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- command: [/testbin/opkg, install, zlib-dev=1.2.11-6]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: |
|
||||
Installing zlib-dev (1.2.11-6) to root...
|
||||
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib-dev_1.2.11-6_mips_24kc.ipk
|
||||
Installing zlib (1.2.11-6) to root...
|
||||
Downloading https://downloads.openwrt.org/releases/22.03.0/packages/mips_24kc/base/zlib_1.2.11-6_mips_24kc.ipk
|
||||
Configuring zlib.
|
||||
Configuring zlib-dev.
|
||||
err: ""
|
||||
- command: [/testbin/opkg, list-installed, zlib-dev]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "zlib-dev - 1.2.11-6 \n" # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
|
||||
err: ""
|
||||
- id: install_vim_updatecache
|
||||
input:
|
||||
name: vim-fuller
|
||||
state: present
|
||||
update_cache: true
|
||||
output:
|
||||
msg: installed 1 package(s)
|
||||
run_command_calls:
|
||||
- command: [/testbin/opkg, update]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- command: [/testbin/opkg, list-installed, vim-fuller]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- command: [/testbin/opkg, install, vim-fuller]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: |
|
||||
Multiple packages (libgcc1 and libgcc1) providing same name marked HOLD or PREFER. Using latest.
|
||||
Installing vim-fuller (9.0-1) to root...
|
||||
Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/packages/vim-fuller_9.0-1_x86_64.ipk
|
||||
Installing terminfo (6.4-2) to root...
|
||||
Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/terminfo_6.4-2_x86_64.ipk
|
||||
Installing libncurses6 (6.4-2) to root...
|
||||
Downloading https://downloads.openwrt.org/snapshots/packages/x86_64/base/libncurses6_6.4-2_x86_64.ipk
|
||||
Configuring terminfo.
|
||||
Configuring libncurses6.
|
||||
Configuring vim-fuller.
|
||||
err: ""
|
||||
- command: [/testbin/opkg, list-installed, vim-fuller]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "vim-fuller - 9.0-1 \n" # This output has the extra space at the end, to satisfy the behaviour of Yocto/OpenEmbedded's opkg
|
||||
err: ""
|
||||
@@ -7,6 +7,10 @@ __metaclass__ = type
|
||||
|
||||
from ansible_collections.community.general.tests.unit.compat import unittest
|
||||
from ansible_collections.community.general.plugins.modules import pagerduty_alert
|
||||
import json
|
||||
import pytest
|
||||
from ansible_collections.community.general.tests.unit.compat.mock import patch
|
||||
from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args
|
||||
|
||||
|
||||
class PagerDutyAlertsTest(unittest.TestCase):
|
||||
@@ -44,3 +48,106 @@ class PagerDutyAlertsTest(unittest.TestCase):
|
||||
class Response(object):
|
||||
def read(self):
|
||||
return '{"incidents":[{"id": "incident_id", "status": "triggered"}]}'
|
||||
|
||||
|
||||
class TestPagerDutyAlertModule(ModuleTestCase):
|
||||
def setUp(self):
|
||||
super(TestPagerDutyAlertModule, self).setUp()
|
||||
self.module = pagerduty_alert
|
||||
|
||||
def tearDown(self):
|
||||
super(TestPagerDutyAlertModule, self).tearDown()
|
||||
|
||||
@pytest.fixture
|
||||
def fetch_url_mock(self, mocker):
|
||||
return mocker.patch('ansible.module_utils.monitoring.pagerduty_change.fetch_url')
|
||||
|
||||
def test_module_fail_when_required_args_missing(self):
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({})
|
||||
self.module.main()
|
||||
|
||||
def test_ensure_alert_created_with_minimal_data(self):
|
||||
set_module_args({
|
||||
'state': 'triggered',
|
||||
'api_version': 'v2',
|
||||
'integration_key': 'test',
|
||||
'source': 'My Ansible Script',
|
||||
'desc': 'Description for alert'
|
||||
})
|
||||
|
||||
with patch.object(pagerduty_alert, 'fetch_url') as fetch_url_mock:
|
||||
fetch_url_mock.return_value = (Response(), {"status": 202})
|
||||
with self.assertRaises(AnsibleExitJson):
|
||||
self.module.main()
|
||||
|
||||
assert fetch_url_mock.call_count == 1
|
||||
url = fetch_url_mock.call_args[0][1]
|
||||
json_data = fetch_url_mock.call_args[1]['data']
|
||||
data = json.loads(json_data)
|
||||
|
||||
assert url == 'https://events.pagerduty.com/v2/enqueue'
|
||||
assert data['routing_key'] == 'test'
|
||||
assert data['event_action'] == 'trigger'
|
||||
assert data['payload']['summary'] == 'Description for alert'
|
||||
assert data['payload']['source'] == 'My Ansible Script'
|
||||
assert data['payload']['severity'] == 'critical'
|
||||
assert data['payload']['timestamp'] is not None
|
||||
|
||||
def test_ensure_alert_created_with_full_data(self):
|
||||
set_module_args({
|
||||
'api_version': 'v2',
|
||||
'component': 'mysql',
|
||||
'custom_details': {'environment': 'production', 'notes': 'this is a test note'},
|
||||
'desc': 'Description for alert',
|
||||
'incident_class': 'ping failure',
|
||||
'integration_key': 'test',
|
||||
'link_url': 'https://pagerduty.com',
|
||||
'link_text': 'PagerDuty',
|
||||
'state': 'triggered',
|
||||
'source': 'My Ansible Script',
|
||||
})
|
||||
|
||||
with patch.object(pagerduty_alert, 'fetch_url') as fetch_url_mock:
|
||||
fetch_url_mock.return_value = (Response(), {"status": 202})
|
||||
with self.assertRaises(AnsibleExitJson):
|
||||
self.module.main()
|
||||
|
||||
assert fetch_url_mock.call_count == 1
|
||||
url = fetch_url_mock.call_args[0][1]
|
||||
json_data = fetch_url_mock.call_args[1]['data']
|
||||
data = json.loads(json_data)
|
||||
|
||||
assert url == 'https://events.pagerduty.com/v2/enqueue'
|
||||
assert data['routing_key'] == 'test'
|
||||
assert data['payload']['summary'] == 'Description for alert'
|
||||
assert data['payload']['source'] == 'My Ansible Script'
|
||||
assert data['payload']['class'] == 'ping failure'
|
||||
assert data['payload']['component'] == 'mysql'
|
||||
assert data['payload']['custom_details']['environment'] == 'production'
|
||||
assert data['payload']['custom_details']['notes'] == 'this is a test note'
|
||||
assert data['links'][0]['href'] == 'https://pagerduty.com'
|
||||
assert data['links'][0]['text'] == 'PagerDuty'
|
||||
|
||||
def test_ensure_alert_acknowledged(self):
|
||||
set_module_args({
|
||||
'state': 'acknowledged',
|
||||
'api_version': 'v2',
|
||||
'integration_key': 'test',
|
||||
'incident_key': 'incident_test_id',
|
||||
})
|
||||
|
||||
with patch.object(pagerduty_alert, 'fetch_url') as fetch_url_mock:
|
||||
fetch_url_mock.return_value = (Response(), {"status": 202})
|
||||
with self.assertRaises(AnsibleExitJson):
|
||||
self.module.main()
|
||||
|
||||
assert fetch_url_mock.call_count == 1
|
||||
url = fetch_url_mock.call_args[0][1]
|
||||
json_data = fetch_url_mock.call_args[1]['data']
|
||||
data = json.loads(json_data)
|
||||
|
||||
assert url == 'https://events.pagerduty.com/v2/enqueue'
|
||||
assert data['routing_key'] == 'test'
|
||||
assert data['event_action'] == 'acknowledge'
|
||||
assert data['dedup_key'] == 'incident_test_id'
|
||||
|
||||
@@ -12,216 +12,26 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from collections import namedtuple
|
||||
from ansible_collections.community.general.plugins.modules import puppet
|
||||
|
||||
import pytest
|
||||
|
||||
TESTED_MODULE = puppet.__name__
|
||||
from ansible_collections.community.general.plugins.modules import puppet as module
|
||||
from .cmd_runner_test_utils import CmdRunnerTestHelper
|
||||
|
||||
|
||||
ModuleTestCase = namedtuple("ModuleTestCase", ["id", "input", "output", "run_command_calls"])
|
||||
RunCmdCall = namedtuple("RunCmdCall", ["command", "environ", "rc", "out", "err"])
|
||||
with open("tests/unit/plugins/modules/test_puppet.yaml", "r") as TEST_CASES:
|
||||
helper = CmdRunnerTestHelper(module.main, test_cases=TEST_CASES)
|
||||
patch_bin = helper.cmd_fixture
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_get_bin_path(mocker):
|
||||
@pytest.mark.parametrize('patch_ansible_module, testcase',
|
||||
helper.testcases_params, ids=helper.testcases_ids,
|
||||
indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_module(mocker, capfd, patch_bin, testcase):
|
||||
"""
|
||||
Function used for mocking AnsibleModule.get_bin_path
|
||||
"""
|
||||
def mockie(self, path, *args, **kwargs):
|
||||
return "/testbin/{0}".format(path)
|
||||
mocker.patch("ansible.module_utils.basic.AnsibleModule.get_bin_path", mockie)
|
||||
|
||||
|
||||
TEST_CASES = [
|
||||
ModuleTestCase(
|
||||
id="puppet_agent_plain",
|
||||
input={},
|
||||
output=dict(changed=False),
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="blah, anything",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=[
|
||||
"/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
|
||||
"--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0"
|
||||
],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
]
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="puppet_agent_certname",
|
||||
input={"certname": "potatobox"},
|
||||
output=dict(changed=False),
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="blah, anything",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=[
|
||||
"/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
|
||||
"--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--certname=potatobox"
|
||||
],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
]
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="puppet_agent_tags_abc",
|
||||
input={"tags": ["a", "b", "c"]},
|
||||
output=dict(changed=False),
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="blah, anything",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=[
|
||||
"/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
|
||||
"--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--tags", "a,b,c"
|
||||
],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
]
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="puppet_agent_skip_tags_def",
|
||||
input={"skip_tags": ["d", "e", "f"]},
|
||||
output=dict(changed=False),
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="blah, anything",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=[
|
||||
"/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
|
||||
"--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--skip_tags", "d,e,f"
|
||||
],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
]
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="puppet_agent_noop_false",
|
||||
input={"noop": False},
|
||||
output=dict(changed=False),
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="blah, anything",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=[
|
||||
"/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
|
||||
"--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--no-noop"
|
||||
],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
]
|
||||
),
|
||||
ModuleTestCase(
|
||||
id="puppet_agent_noop_true",
|
||||
input={"noop": True},
|
||||
output=dict(changed=False),
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=["/testbin/puppet", "config", "print", "agent_disabled_lockfile"],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="blah, anything",
|
||||
err="",
|
||||
),
|
||||
RunCmdCall(
|
||||
command=[
|
||||
"/testbin/timeout", "-s", "9", "30m", "/testbin/puppet", "agent", "--onetime", "--no-daemonize",
|
||||
"--no-usecacheonfailure", "--no-splay", "--detailed-exitcodes", "--verbose", "--color", "0", "--noop"
|
||||
],
|
||||
environ={'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
rc=0,
|
||||
out="",
|
||||
err="",
|
||||
),
|
||||
]
|
||||
),
|
||||
]
|
||||
TEST_CASES_IDS = [item.id for item in TEST_CASES]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("patch_ansible_module, testcase",
|
||||
[[x.input, x] for x in TEST_CASES],
|
||||
ids=TEST_CASES_IDS,
|
||||
indirect=["patch_ansible_module"])
|
||||
@pytest.mark.usefixtures("patch_ansible_module")
|
||||
def test_puppet(mocker, capfd, patch_get_bin_path, testcase):
|
||||
"""
|
||||
Run unit tests for test cases listen in TEST_CASES
|
||||
Run unit tests for test cases listed in TEST_CASES
|
||||
"""
|
||||
|
||||
run_cmd_calls = testcase.run_command_calls
|
||||
|
||||
# Mock function used for running commands first
|
||||
call_results = [(x.rc, x.out, x.err) for x in run_cmd_calls]
|
||||
mock_run_command = mocker.patch(
|
||||
"ansible.module_utils.basic.AnsibleModule.run_command",
|
||||
side_effect=call_results)
|
||||
|
||||
# Try to run test case
|
||||
with pytest.raises(SystemExit):
|
||||
puppet.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
print("testcase =\n%s" % str(testcase))
|
||||
print("results =\n%s" % results)
|
||||
|
||||
assert mock_run_command.call_count == len(run_cmd_calls)
|
||||
if mock_run_command.call_count:
|
||||
call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
|
||||
expected_call_args_list = [(item.command, item.environ) for item in run_cmd_calls]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
assert call_args_list == expected_call_args_list
|
||||
|
||||
assert results.get("changed", False) == testcase.output["changed"]
|
||||
if "failed" in testcase:
|
||||
assert results.get("failed", False) == testcase.output["failed"]
|
||||
if "msg" in testcase:
|
||||
assert results.get("msg", "") == testcase.output["msg"]
|
||||
with helper(testcase, mocker, capfd) as testcase_context:
|
||||
testcase_context.run()
|
||||
|
||||
192
tests/unit/plugins/modules/test_puppet.yaml
Normal file
192
tests/unit/plugins/modules/test_puppet.yaml
Normal file
@@ -0,0 +1,192 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Alexei Znamensky (russoz@gmail.com)
|
||||
# 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
|
||||
|
||||
---
|
||||
- id: puppet_agent_plain
|
||||
input: {}
|
||||
output:
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/puppet, config, print, agent_disabled_lockfile]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "blah, anything"
|
||||
err: ""
|
||||
- command:
|
||||
- /testbin/timeout
|
||||
- -s
|
||||
- "9"
|
||||
- 30m
|
||||
- /testbin/puppet
|
||||
- agent
|
||||
- --onetime
|
||||
- --no-daemonize
|
||||
- --no-usecacheonfailure
|
||||
- --no-splay
|
||||
- --detailed-exitcodes
|
||||
- --verbose
|
||||
- --color
|
||||
- "0"
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: puppet_agent_certname
|
||||
input:
|
||||
certname: potatobox
|
||||
output:
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/puppet, config, print, agent_disabled_lockfile]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "blah, anything"
|
||||
err: ""
|
||||
- command:
|
||||
- /testbin/timeout
|
||||
- -s
|
||||
- "9"
|
||||
- 30m
|
||||
- /testbin/puppet
|
||||
- agent
|
||||
- --onetime
|
||||
- --no-daemonize
|
||||
- --no-usecacheonfailure
|
||||
- --no-splay
|
||||
- --detailed-exitcodes
|
||||
- --verbose
|
||||
- --color
|
||||
- "0"
|
||||
- --certname=potatobox
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: puppet_agent_tags_abc
|
||||
input:
|
||||
tags: [a, b, c]
|
||||
output:
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/puppet, config, print, agent_disabled_lockfile]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "blah, anything"
|
||||
err: ""
|
||||
- command:
|
||||
- /testbin/timeout
|
||||
- -s
|
||||
- "9"
|
||||
- 30m
|
||||
- /testbin/puppet
|
||||
- agent
|
||||
- --onetime
|
||||
- --no-daemonize
|
||||
- --no-usecacheonfailure
|
||||
- --no-splay
|
||||
- --detailed-exitcodes
|
||||
- --verbose
|
||||
- --color
|
||||
- "0"
|
||||
- --tags
|
||||
- a,b,c
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: puppet_agent_skip_tags_def
|
||||
input:
|
||||
skip_tags: [d, e, f]
|
||||
output:
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/puppet, config, print, agent_disabled_lockfile]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "blah, anything"
|
||||
err: ""
|
||||
- command:
|
||||
- /testbin/timeout
|
||||
- -s
|
||||
- "9"
|
||||
- 30m
|
||||
- /testbin/puppet
|
||||
- agent
|
||||
- --onetime
|
||||
- --no-daemonize
|
||||
- --no-usecacheonfailure
|
||||
- --no-splay
|
||||
- --detailed-exitcodes
|
||||
- --verbose
|
||||
- --color
|
||||
- "0"
|
||||
- --skip_tags
|
||||
- d,e,f
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: puppet_agent_noop_false
|
||||
input:
|
||||
noop: false
|
||||
output:
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/puppet, config, print, agent_disabled_lockfile]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "blah, anything"
|
||||
err: ""
|
||||
- command:
|
||||
- /testbin/timeout
|
||||
- -s
|
||||
- "9"
|
||||
- 30m
|
||||
- /testbin/puppet
|
||||
- agent
|
||||
- --onetime
|
||||
- --no-daemonize
|
||||
- --no-usecacheonfailure
|
||||
- --no-splay
|
||||
- --detailed-exitcodes
|
||||
- --verbose
|
||||
- --color
|
||||
- "0"
|
||||
- --no-noop
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: puppet_agent_noop_true
|
||||
input:
|
||||
noop: true
|
||||
output:
|
||||
changed: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/puppet, config, print, agent_disabled_lockfile]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "blah, anything"
|
||||
err: ""
|
||||
- command:
|
||||
- /testbin/timeout
|
||||
- -s
|
||||
- "9"
|
||||
- 30m
|
||||
- /testbin/puppet
|
||||
- agent
|
||||
- --onetime
|
||||
- --no-daemonize
|
||||
- --no-usecacheonfailure
|
||||
- --no-splay
|
||||
- --detailed-exitcodes
|
||||
- --verbose
|
||||
- --color
|
||||
- "0"
|
||||
- --noop
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
@@ -6,28 +6,10 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from collections import namedtuple
|
||||
from ansible_collections.community.general.plugins.modules import snap
|
||||
|
||||
import pytest
|
||||
|
||||
TESTED_MODULE = snap.__name__
|
||||
|
||||
|
||||
ModuleTestCase = namedtuple("ModuleTestCase", ["id", "input", "output", "run_command_calls"])
|
||||
RunCmdCall = namedtuple("RunCmdCall", ["command", "environ", "rc", "out", "err"])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_get_bin_path(mocker):
|
||||
"""
|
||||
Function used for mocking AnsibleModule.get_bin_path
|
||||
"""
|
||||
def mockie(self, path, *args, **kwargs):
|
||||
return "/testbin/{0}".format(path)
|
||||
mocker.patch("ansible.module_utils.basic.AnsibleModule.get_bin_path", mockie)
|
||||
from .cmd_runner_test_utils import CmdRunnerTestHelper, ModuleTestCase, RunCmdCall
|
||||
from ansible_collections.community.general.plugins.modules import snap as module
|
||||
|
||||
|
||||
issue_6803_status_out = """Name Version Rev Tracking Publisher Notes
|
||||
@@ -399,6 +381,7 @@ TEST_CASES = [
|
||||
id="simple case",
|
||||
input={"name": ["hello-world"]},
|
||||
output=dict(changed=True, snaps_installed=["hello-world"]),
|
||||
flags={},
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=['/testbin/snap', 'info', 'hello-world'],
|
||||
@@ -427,6 +410,7 @@ TEST_CASES = [
|
||||
id="issue_6803",
|
||||
input={"name": ["microk8s", "kubectl"], "classic": True},
|
||||
output=dict(changed=True, snaps_installed=["microk8s", "kubectl"]),
|
||||
flags={},
|
||||
run_command_calls=[
|
||||
RunCmdCall(
|
||||
command=['/testbin/snap', 'info', 'microk8s', 'kubectl'],
|
||||
@@ -459,50 +443,20 @@ TEST_CASES = [
|
||||
]
|
||||
),
|
||||
]
|
||||
TEST_CASES_IDS = [item.id for item in TEST_CASES]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("patch_ansible_module, testcase",
|
||||
[[x.input, x] for x in TEST_CASES],
|
||||
ids=TEST_CASES_IDS,
|
||||
indirect=["patch_ansible_module"])
|
||||
@pytest.mark.usefixtures("patch_ansible_module")
|
||||
def test_snap(mocker, capfd, patch_get_bin_path, testcase):
|
||||
helper = CmdRunnerTestHelper(module.main, test_cases=TEST_CASES)
|
||||
patch_bin = helper.cmd_fixture
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module, testcase',
|
||||
helper.testcases_params, ids=helper.testcases_ids,
|
||||
indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_module(mocker, capfd, patch_bin, testcase):
|
||||
"""
|
||||
Run unit tests for test cases listen in TEST_CASES
|
||||
Run unit tests for test cases listed in TEST_CASES
|
||||
"""
|
||||
|
||||
run_cmd_calls = testcase.run_command_calls
|
||||
|
||||
# Mock function used for running commands first
|
||||
call_results = [(x.rc, x.out, x.err) for x in run_cmd_calls]
|
||||
mock_run_command = mocker.patch(
|
||||
"ansible.module_utils.basic.AnsibleModule.run_command",
|
||||
side_effect=call_results)
|
||||
|
||||
# Try to run test case
|
||||
with pytest.raises(SystemExit):
|
||||
snap.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
print("testcase =\n%s" % str(testcase))
|
||||
print("results =\n%s" % results)
|
||||
|
||||
assert mock_run_command.call_count == len(run_cmd_calls)
|
||||
if mock_run_command.call_count:
|
||||
call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
|
||||
expected_call_args_list = [(item.command, item.environ) for item in run_cmd_calls]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
try:
|
||||
assert call_args_list == expected_call_args_list
|
||||
except AssertionError:
|
||||
for test_call_run, expected_call_run in zip(call_args_list, expected_call_args_list):
|
||||
assert test_call_run == expected_call_run
|
||||
|
||||
assert results.get("changed", False) == testcase.output["changed"]
|
||||
if "failed" in testcase:
|
||||
assert results.get("failed", False) == testcase.output["failed"]
|
||||
if "msg" in testcase:
|
||||
assert results.get("msg", "") == testcase.output["msg"]
|
||||
with helper(testcase, mocker, capfd) as testcase_context:
|
||||
testcase_context.run()
|
||||
|
||||
@@ -12,301 +12,26 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import xfconf
|
||||
|
||||
import pytest
|
||||
|
||||
TESTED_MODULE = xfconf.__name__
|
||||
from ansible_collections.community.general.plugins.modules import xfconf as module
|
||||
from .cmd_runner_test_utils import CmdRunnerTestHelper
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_xfconf(mocker):
|
||||
"""
|
||||
Function used for mocking some parts of redhat_subscription module
|
||||
"""
|
||||
mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path',
|
||||
return_value='/testbin/xfconf-query')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_without_required_parameters(capfd, patch_xfconf):
|
||||
"""
|
||||
Failure must occurs when all parameters are missing
|
||||
"""
|
||||
with pytest.raises(SystemExit):
|
||||
xfconf.main()
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
assert results['failed']
|
||||
assert 'missing required arguments' in results['msg']
|
||||
|
||||
|
||||
TEST_CASES = [
|
||||
[
|
||||
{
|
||||
'channel': 'xfwm4',
|
||||
'property': '/general/inactive_opacity',
|
||||
'state': 'present',
|
||||
'value_type': 'int',
|
||||
'value': 90,
|
||||
},
|
||||
{
|
||||
'id': 'test_property_set_property',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '100\n', '',),
|
||||
),
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity',
|
||||
'--create', '--type', 'int', '--set', '90'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '', '',),
|
||||
),
|
||||
],
|
||||
'changed': True,
|
||||
'previous_value': '100',
|
||||
'value_type': 'int',
|
||||
'value': '90',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
'channel': 'xfwm4',
|
||||
'property': '/general/inactive_opacity',
|
||||
'state': 'present',
|
||||
'value_type': 'int',
|
||||
'value': 90,
|
||||
},
|
||||
{
|
||||
'id': 'test_property_set_property_same_value',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '90\n', '',),
|
||||
),
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity',
|
||||
'--create', '--type', 'int', '--set', '90'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '', '',),
|
||||
),
|
||||
],
|
||||
'changed': False,
|
||||
'previous_value': '90',
|
||||
'value_type': 'int',
|
||||
'value': '90',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
'channel': 'xfce4-session',
|
||||
'property': '/general/SaveOnExit',
|
||||
'state': 'present',
|
||||
'value_type': 'bool',
|
||||
'value': False,
|
||||
},
|
||||
{
|
||||
'id': 'test_property_set_property_bool_false',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfce4-session', '--property', '/general/SaveOnExit'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, 'true\n', '',),
|
||||
),
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfce4-session', '--property', '/general/SaveOnExit',
|
||||
'--create', '--type', 'bool', '--set', 'false'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, 'false\n', '',),
|
||||
),
|
||||
],
|
||||
'changed': True,
|
||||
'previous_value': 'true',
|
||||
'value_type': 'bool',
|
||||
'value': 'False',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
'channel': 'xfwm4',
|
||||
'property': '/general/workspace_names',
|
||||
'state': 'present',
|
||||
'value_type': 'string',
|
||||
'value': ['A', 'B', 'C'],
|
||||
},
|
||||
{
|
||||
'id': 'test_property_set_array',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, 'Value is an array with 3 items:\n\nMain\nWork\nTmp\n', '',),
|
||||
),
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names',
|
||||
'--create', '--force-array', '--type', 'string', '--set', 'A', '--type', 'string', '--set', 'B',
|
||||
'--type', 'string', '--set', 'C'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '', '',),
|
||||
),
|
||||
],
|
||||
'changed': True,
|
||||
'previous_value': ['Main', 'Work', 'Tmp'],
|
||||
'value_type': ['str', 'str', 'str'],
|
||||
'value': ['A', 'B', 'C'],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
'channel': 'xfwm4',
|
||||
'property': '/general/workspace_names',
|
||||
'state': 'present',
|
||||
'value_type': 'string',
|
||||
'value': ['A', 'B', 'C'],
|
||||
},
|
||||
{
|
||||
'id': 'test_property_set_array_to_same_value',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, 'Value is an array with 3 items:\n\nA\nB\nC\n', '',),
|
||||
),
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names',
|
||||
'--create', '--force-array', '--type', 'string', '--set', 'A', '--type', 'string', '--set', 'B',
|
||||
'--type', 'string', '--set', 'C'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '', '',),
|
||||
),
|
||||
],
|
||||
'changed': False,
|
||||
'previous_value': ['A', 'B', 'C'],
|
||||
'value_type': ['str', 'str', 'str'],
|
||||
'value': ['A', 'B', 'C'],
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
'channel': 'xfwm4',
|
||||
'property': '/general/workspace_names',
|
||||
'state': 'absent',
|
||||
},
|
||||
{
|
||||
'id': 'test_property_reset_value',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, 'Value is an array with 3 items:\n\nA\nB\nC\n', '',),
|
||||
),
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names',
|
||||
'--reset'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': False},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '', '',),
|
||||
),
|
||||
],
|
||||
'changed': True,
|
||||
'previous_value': ['A', 'B', 'C'],
|
||||
'value_type': None,
|
||||
'value': None,
|
||||
},
|
||||
],
|
||||
]
|
||||
TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
|
||||
with open("tests/unit/plugins/modules/test_xfconf.yaml", "r") as TEST_CASES:
|
||||
helper = CmdRunnerTestHelper(module.main, test_cases=TEST_CASES)
|
||||
patch_bin = helper.cmd_fixture
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module, testcase',
|
||||
TEST_CASES,
|
||||
ids=TEST_CASES_IDS,
|
||||
helper.testcases_params, ids=helper.testcases_ids,
|
||||
indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_xfconf(mocker, capfd, patch_xfconf, testcase):
|
||||
def test_module(mocker, capfd, patch_bin, testcase):
|
||||
"""
|
||||
Run unit tests for test cases listen in TEST_CASES
|
||||
Run unit tests for test cases listed in TEST_CASES
|
||||
"""
|
||||
|
||||
# Mock function used for running commands first
|
||||
call_results = [item[2] for item in testcase['run_command.calls']]
|
||||
mock_run_command = mocker.patch(
|
||||
'ansible.module_utils.basic.AnsibleModule.run_command',
|
||||
side_effect=call_results)
|
||||
|
||||
# Try to run test case
|
||||
with pytest.raises(SystemExit):
|
||||
xfconf.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
print("testcase =\n%s" % testcase)
|
||||
print("results =\n%s" % results)
|
||||
|
||||
assert 'changed' in results
|
||||
assert results['changed'] == testcase['changed']
|
||||
|
||||
for test_result in ('channel', 'property'):
|
||||
assert test_result in results, "'{0}' not found in {1}".format(test_result, results)
|
||||
assert results[test_result] == results['invocation']['module_args'][test_result], \
|
||||
"'{0}': '{1}' != '{2}'".format(test_result, results[test_result], results['invocation']['module_args'][test_result])
|
||||
|
||||
assert mock_run_command.call_count == len(testcase['run_command.calls'])
|
||||
if mock_run_command.call_count:
|
||||
call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
|
||||
expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
assert call_args_list == expected_call_args_list
|
||||
|
||||
expected_cmd, dummy, expected_res = testcase['run_command.calls'][-1]
|
||||
assert results['cmd'] == expected_cmd
|
||||
assert results['stdout'] == expected_res[1]
|
||||
assert results['stderr'] == expected_res[2]
|
||||
|
||||
for conditional_test_result in ('msg', 'value', 'previous_value'):
|
||||
if conditional_test_result in testcase:
|
||||
assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results)
|
||||
assert results[conditional_test_result] == testcase[conditional_test_result], \
|
||||
"'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result])
|
||||
with helper(testcase, mocker, capfd) as testcase_context:
|
||||
testcase_context.run()
|
||||
|
||||
185
tests/unit/plugins/modules/test_xfconf.yaml
Normal file
185
tests/unit/plugins/modules/test_xfconf.yaml
Normal file
@@ -0,0 +1,185 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Alexei Znamensky (russoz@gmail.com)
|
||||
# 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
|
||||
|
||||
---
|
||||
- id: test_missing_input
|
||||
input: {}
|
||||
output:
|
||||
failed: true
|
||||
msg: "missing required arguments: channel, property"
|
||||
- id: test_property_set_property
|
||||
input:
|
||||
channel: xfwm4
|
||||
property: /general/inactive_opacity
|
||||
state: present
|
||||
value_type: int
|
||||
value: 90
|
||||
output:
|
||||
changed: true
|
||||
previous_value: '100'
|
||||
type: int
|
||||
value: '90'
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "100\n"
|
||||
err: ""
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity, --create, --type, int, --set, '90']
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: test_property_set_property_same_value
|
||||
input:
|
||||
channel: xfwm4
|
||||
property: /general/inactive_opacity
|
||||
state: present
|
||||
value_type: int
|
||||
value: 90
|
||||
output:
|
||||
changed: false
|
||||
previous_value: '90'
|
||||
type: int
|
||||
value: '90'
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "90\n"
|
||||
err: ""
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity, --create, --type, int, --set, '90']
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: test_property_set_property_bool_false
|
||||
input:
|
||||
channel: xfce4-session
|
||||
property: /general/SaveOnExit
|
||||
state: present
|
||||
value_type: bool
|
||||
value: False
|
||||
output:
|
||||
changed: true
|
||||
previous_value: 'true'
|
||||
type: bool
|
||||
value: 'False'
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfce4-session, --property, /general/SaveOnExit]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "true\n"
|
||||
err: ""
|
||||
- command: [/testbin/xfconf-query, --channel, xfce4-session, --property, /general/SaveOnExit, --create, --type, bool, --set, 'false']
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "false\n"
|
||||
err: ""
|
||||
- id: test_property_set_array
|
||||
input:
|
||||
channel: xfwm4
|
||||
property: /general/workspace_names
|
||||
state: present
|
||||
value_type: string
|
||||
value: [A, B, C]
|
||||
output:
|
||||
changed: true
|
||||
previous_value: [Main, Work, Tmp]
|
||||
type: [string, string, string]
|
||||
value: [A, B, C]
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "Value is an array with 3 items:\n\nMain\nWork\nTmp\n"
|
||||
err: ""
|
||||
- command:
|
||||
- /testbin/xfconf-query
|
||||
- --channel
|
||||
- xfwm4
|
||||
- --property
|
||||
- /general/workspace_names
|
||||
- --create
|
||||
- --force-array
|
||||
- --type
|
||||
- string
|
||||
- --set
|
||||
- A
|
||||
- --type
|
||||
- string
|
||||
- --set
|
||||
- B
|
||||
- --type
|
||||
- string
|
||||
- --set
|
||||
- C
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: test_property_set_array_to_same_value
|
||||
input:
|
||||
channel: xfwm4
|
||||
property: /general/workspace_names
|
||||
state: present
|
||||
value_type: string
|
||||
value: [A, B, C]
|
||||
output:
|
||||
changed: false
|
||||
previous_value: [A, B, C]
|
||||
type: [string, string, string]
|
||||
value: [A, B, C]
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "Value is an array with 3 items:\n\nA\nB\nC\n"
|
||||
err: ""
|
||||
- command:
|
||||
- /testbin/xfconf-query
|
||||
- --channel
|
||||
- xfwm4
|
||||
- --property
|
||||
- /general/workspace_names
|
||||
- --create
|
||||
- --force-array
|
||||
- --type
|
||||
- string
|
||||
- --set
|
||||
- A
|
||||
- --type
|
||||
- string
|
||||
- --set
|
||||
- B
|
||||
- --type
|
||||
- string
|
||||
- --set
|
||||
- C
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
- id: test_property_reset_value
|
||||
input:
|
||||
channel: xfwm4
|
||||
property: /general/workspace_names
|
||||
state: absent
|
||||
output:
|
||||
changed: true
|
||||
previous_value: [A, B, C]
|
||||
type: null
|
||||
value: null
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: "Value is an array with 3 items:\n\nA\nB\nC\n"
|
||||
err: ""
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names, --reset]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: false}
|
||||
rc: 0
|
||||
out: ""
|
||||
err: ""
|
||||
@@ -5,168 +5,26 @@
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
|
||||
from ansible_collections.community.general.plugins.modules import xfconf_info
|
||||
|
||||
import pytest
|
||||
|
||||
TESTED_MODULE = xfconf_info.__name__
|
||||
from ansible_collections.community.general.plugins.modules import xfconf_info as module
|
||||
from .cmd_runner_test_utils import CmdRunnerTestHelper
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def patch_xfconf_info(mocker):
|
||||
"""
|
||||
Function used for mocking some parts of redhat_subscription module
|
||||
"""
|
||||
mocker.patch('ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.get_bin_path',
|
||||
return_value='/testbin/xfconf-query')
|
||||
|
||||
|
||||
TEST_CASES = [
|
||||
[
|
||||
{'channel': 'xfwm4', 'property': '/general/inactive_opacity'},
|
||||
{
|
||||
'id': 'test_simple_property_get',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/inactive_opacity'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '100\n', '',),
|
||||
),
|
||||
],
|
||||
'is_array': False,
|
||||
'value': '100',
|
||||
}
|
||||
],
|
||||
[
|
||||
{'channel': 'xfwm4', 'property': '/general/i_dont_exist'},
|
||||
{
|
||||
'id': 'test_simple_property_get_nonexistent',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/i_dont_exist'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(1, '', 'Property "/general/i_dont_exist" does not exist on channel "xfwm4".\n',),
|
||||
),
|
||||
],
|
||||
'is_array': False,
|
||||
}
|
||||
],
|
||||
[
|
||||
{'property': '/general/i_dont_exist'},
|
||||
{
|
||||
'id': 'test_property_no_channel',
|
||||
'run_command.calls': [],
|
||||
}
|
||||
],
|
||||
[
|
||||
{'channel': 'xfwm4', 'property': '/general/workspace_names'},
|
||||
{
|
||||
'id': 'test_property_get_array',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--channel', 'xfwm4', '--property', '/general/workspace_names'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, 'Value is an array with 3 items:\n\nMain\nWork\nTmp\n', '',),
|
||||
),
|
||||
],
|
||||
'is_array': True,
|
||||
'value_array': ['Main', 'Work', 'Tmp'],
|
||||
},
|
||||
],
|
||||
[
|
||||
{},
|
||||
{
|
||||
'id': 'get_channels',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--list'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, 'Channels:\n a\n b\n c\n', '',),
|
||||
),
|
||||
],
|
||||
'is_array': False,
|
||||
'channels': ['a', 'b', 'c'],
|
||||
},
|
||||
],
|
||||
[
|
||||
{'channel': 'xfwm4'},
|
||||
{
|
||||
'id': 'get_properties',
|
||||
'run_command.calls': [
|
||||
(
|
||||
# Calling of following command will be asserted
|
||||
['/testbin/xfconf-query', '--list', '--channel', 'xfwm4'],
|
||||
# Was return code checked?
|
||||
{'environ_update': {'LANGUAGE': 'C', 'LC_ALL': 'C'}, 'check_rc': True},
|
||||
# Mock of returned code, stdout and stderr
|
||||
(0, '/general/wrap_cycle\n/general/wrap_layout\n/general/wrap_resistance\n/general/wrap_windows\n'
|
||||
'/general/wrap_workspaces\n/general/zoom_desktop\n', '',),
|
||||
),
|
||||
],
|
||||
'is_array': False,
|
||||
'properties': [
|
||||
'/general/wrap_cycle',
|
||||
'/general/wrap_layout',
|
||||
'/general/wrap_resistance',
|
||||
'/general/wrap_windows',
|
||||
'/general/wrap_workspaces',
|
||||
'/general/zoom_desktop',
|
||||
],
|
||||
},
|
||||
],
|
||||
]
|
||||
TEST_CASES_IDS = [item[1]['id'] for item in TEST_CASES]
|
||||
with open("tests/unit/plugins/modules/test_xfconf_info.yaml", "r") as TEST_CASES:
|
||||
helper = CmdRunnerTestHelper(module.main, test_cases=TEST_CASES)
|
||||
patch_bin = helper.cmd_fixture
|
||||
|
||||
|
||||
@pytest.mark.parametrize('patch_ansible_module, testcase',
|
||||
TEST_CASES,
|
||||
ids=TEST_CASES_IDS,
|
||||
helper.testcases_params, ids=helper.testcases_ids,
|
||||
indirect=['patch_ansible_module'])
|
||||
@pytest.mark.usefixtures('patch_ansible_module')
|
||||
def test_xfconf_info(mocker, capfd, patch_xfconf_info, testcase):
|
||||
def test_module(mocker, capfd, patch_bin, testcase):
|
||||
"""
|
||||
Run unit tests for test cases listen in TEST_CASES
|
||||
Run unit tests for test cases listed in TEST_CASES
|
||||
"""
|
||||
|
||||
# Mock function used for running commands first
|
||||
call_results = [item[2] for item in testcase['run_command.calls']]
|
||||
mock_run_command = mocker.patch(
|
||||
'ansible_collections.community.general.plugins.module_utils.mh.module_helper.AnsibleModule.run_command',
|
||||
side_effect=call_results)
|
||||
|
||||
# Try to run test case
|
||||
with pytest.raises(SystemExit):
|
||||
xfconf_info.main()
|
||||
|
||||
out, err = capfd.readouterr()
|
||||
results = json.loads(out)
|
||||
print("testcase =\n%s" % testcase)
|
||||
print("results =\n%s" % results)
|
||||
|
||||
for conditional_test_result in ('value_array', 'value', 'is_array', 'properties', 'channels'):
|
||||
if conditional_test_result in testcase:
|
||||
assert conditional_test_result in results, "'{0}' not found in {1}".format(conditional_test_result, results)
|
||||
assert results[conditional_test_result] == testcase[conditional_test_result], \
|
||||
"'{0}': '{1}' != '{2}'".format(conditional_test_result, results[conditional_test_result], testcase[conditional_test_result])
|
||||
|
||||
assert mock_run_command.call_count == len(testcase['run_command.calls'])
|
||||
if mock_run_command.call_count:
|
||||
call_args_list = [(item[0][0], item[1]) for item in mock_run_command.call_args_list]
|
||||
expected_call_args_list = [(item[0], item[1]) for item in testcase['run_command.calls']]
|
||||
print("call args list =\n%s" % call_args_list)
|
||||
print("expected args list =\n%s" % expected_call_args_list)
|
||||
assert call_args_list == expected_call_args_list
|
||||
with helper(testcase, mocker, capfd) as testcase_context:
|
||||
testcase_context.run()
|
||||
|
||||
83
tests/unit/plugins/modules/test_xfconf_info.yaml
Normal file
83
tests/unit/plugins/modules/test_xfconf_info.yaml
Normal file
@@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) Alexei Znamensky (russoz@gmail.com)
|
||||
# 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
|
||||
|
||||
---
|
||||
- id: test_simple_property_get
|
||||
input:
|
||||
channel: xfwm4
|
||||
property: /general/inactive_opacity
|
||||
output:
|
||||
value: '100'
|
||||
is_array: false
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/inactive_opacity]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "100\n"
|
||||
err: ""
|
||||
- id: test_simple_property_get_nonexistent
|
||||
input:
|
||||
channel: xfwm4
|
||||
property: /general/i_dont_exist
|
||||
output: {}
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/i_dont_exist]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 1
|
||||
out: ""
|
||||
err: 'Property "/general/i_dont_exist" does not exist on channel "xfwm4".\n'
|
||||
- id: test_property_no_channel
|
||||
input:
|
||||
property: /general/i_dont_exist
|
||||
output:
|
||||
failed: true
|
||||
msg: "missing parameter(s) required by 'property': channel"
|
||||
run_command_calls: []
|
||||
- id: test_property_get_array
|
||||
input:
|
||||
channel: xfwm4
|
||||
property: /general/workspace_names
|
||||
output:
|
||||
is_array: true
|
||||
value_array: [Main, Work, Tmp]
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --channel, xfwm4, --property, /general/workspace_names]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "Value is an array with 3 items:\n\nMain\nWork\nTmp\n"
|
||||
err: ""
|
||||
- id: get_channels
|
||||
input: {}
|
||||
output:
|
||||
channels: [a, b, c]
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --list]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: "Channels:\n a\n b\n c\n"
|
||||
err: ""
|
||||
- id: get_properties
|
||||
input:
|
||||
channel: xfwm4
|
||||
output:
|
||||
properties:
|
||||
- /general/wrap_cycle
|
||||
- /general/wrap_layout
|
||||
- /general/wrap_resistance
|
||||
- /general/wrap_windows
|
||||
- /general/wrap_workspaces
|
||||
- /general/zoom_desktop
|
||||
run_command_calls:
|
||||
- command: [/testbin/xfconf-query, --list, --channel, xfwm4]
|
||||
environ: {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true}
|
||||
rc: 0
|
||||
out: |
|
||||
/general/wrap_cycle
|
||||
/general/wrap_layout
|
||||
/general/wrap_resistance
|
||||
/general/wrap_windows
|
||||
/general/wrap_workspaces
|
||||
/general/zoom_desktop
|
||||
err: ""
|
||||
Reference in New Issue
Block a user