mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-29 19:50:25 -07:00
rundeck_acl_policy: fix project acls are put/posted to the wrong endpoint (#10097)
* Fix project acls are put/posted to the wrong endpoint * Add changelog fragment. * Fix 2.7 sanity errors in github * Fix fragment extension and use 2.7 syntax in test * Update changelogs/fragments/10097-fix-rundeck_acl_policy-project-endpoint.yml Co-authored-by: Felix Fontein <felix@fontein.de> * Fix pep8 formatting * Add licensing to unit test --------- Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
2b4cb6dabc
commit
ff0ed6f912
7 changed files with 277 additions and 7 deletions
|
@ -0,0 +1,2 @@
|
|||
bugfixes:
|
||||
- rundeck_acl_policy - ensure that project ACLs are sent to the correct endpoint (https://github.com/ansible-collections/community.general/pull/10097).
|
|
@ -129,11 +129,18 @@ from ansible_collections.community.general.plugins.module_utils.rundeck import (
|
|||
class RundeckACLManager:
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
if module.params.get("project"):
|
||||
self.endpoint = "project/%s/acl/%s.aclpolicy" % (
|
||||
self.module.params["project"],
|
||||
self.module.params["name"],
|
||||
)
|
||||
else:
|
||||
self.endpoint = "system/acl/%s.aclpolicy" % self.module.params["name"]
|
||||
|
||||
def get_acl(self):
|
||||
resp, info = api_request(
|
||||
module=self.module,
|
||||
endpoint="system/acl/%s.aclpolicy" % self.module.params["name"],
|
||||
endpoint=self.endpoint,
|
||||
)
|
||||
|
||||
return resp
|
||||
|
@ -147,7 +154,7 @@ class RundeckACLManager:
|
|||
|
||||
resp, info = api_request(
|
||||
module=self.module,
|
||||
endpoint="system/acl/%s.aclpolicy" % self.module.params["name"],
|
||||
endpoint=self.endpoint,
|
||||
method="POST",
|
||||
data={"contents": self.module.params["policy"]},
|
||||
)
|
||||
|
@ -171,7 +178,7 @@ class RundeckACLManager:
|
|||
|
||||
resp, info = api_request(
|
||||
module=self.module,
|
||||
endpoint="system/acl/%s.aclpolicy" % self.module.params["name"],
|
||||
endpoint=self.endpoint,
|
||||
method="PUT",
|
||||
data={"contents": self.module.params["policy"]},
|
||||
)
|
||||
|
@ -194,7 +201,7 @@ class RundeckACLManager:
|
|||
if not self.module.check_mode:
|
||||
api_request(
|
||||
module=self.module,
|
||||
endpoint="system/acl/%s.aclpolicy" % self.module.params["name"],
|
||||
endpoint=self.endpoint,
|
||||
method="DELETE",
|
||||
)
|
||||
|
||||
|
|
|
@ -6,3 +6,32 @@
|
|||
rundeck_url: http://localhost:4440
|
||||
rundeck_api_version: 39
|
||||
rundeck_job_id: 3b8a6e54-69fb-42b7-b98f-f82e59238478
|
||||
|
||||
system_acl_policy: |
|
||||
description: Test ACL
|
||||
context:
|
||||
application: 'rundeck'
|
||||
for:
|
||||
project:
|
||||
- allow:
|
||||
- read
|
||||
by:
|
||||
group:
|
||||
- users
|
||||
|
||||
project_acl_policy: |
|
||||
description: Test project acl
|
||||
for:
|
||||
resource:
|
||||
- equals:
|
||||
kind: node
|
||||
allow: [read,refresh]
|
||||
- equals:
|
||||
kind: event
|
||||
allow: [read]
|
||||
job:
|
||||
- allow: [run,kill]
|
||||
node:
|
||||
- allow: [read,run]
|
||||
by:
|
||||
group: users
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
RD_USER: admin
|
||||
RD_PASSWORD: admin
|
||||
register: rundeck_api_token
|
||||
retries: 3
|
||||
until: rundeck_api_token.rc == 0
|
||||
changed_when: true
|
||||
|
||||
- name: Create a Rundeck project
|
||||
community.general.rundeck_project:
|
||||
|
@ -24,6 +27,71 @@
|
|||
token: "{{ rundeck_api_token.stdout_lines[-1] }}"
|
||||
state: present
|
||||
|
||||
- name: Create a system ACL
|
||||
community.general.rundeck_acl_policy:
|
||||
name: test_acl
|
||||
api_version: "{{ rundeck_api_version }}"
|
||||
url: "{{ rundeck_url }}"
|
||||
token: "{{ rundeck_api_token.stdout_lines[-1] }}"
|
||||
state: present
|
||||
policy: "{{ system_acl_policy }}"
|
||||
|
||||
- name: Create a project ACL
|
||||
community.general.rundeck_acl_policy:
|
||||
name: test_acl
|
||||
api_version: "{{ rundeck_api_version }}"
|
||||
url: "{{ rundeck_url }}"
|
||||
token: "{{ rundeck_api_token.stdout_lines[-1] }}"
|
||||
state: present
|
||||
policy: "{{ project_acl_policy }}"
|
||||
project: test_project
|
||||
|
||||
- name: Retrieve ACLs
|
||||
ansible.builtin.uri:
|
||||
url: "{{ rundeck_url }}/api/{{ rundeck_api_version }}/{{ item }}"
|
||||
headers:
|
||||
accept: application/json
|
||||
x-rundeck-auth-token: "{{ rundeck_api_token.stdout_lines[-1] }}"
|
||||
register: acl_policy_check
|
||||
loop:
|
||||
- system/acl/test_acl.aclpolicy
|
||||
- project/test_project/acl/test_acl.aclpolicy
|
||||
|
||||
- name: Assert ACL content is correct
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- acl_policy_check['results'][0]['json']['contents'] == system_acl_policy
|
||||
- acl_policy_check['results'][1]['json']['contents'] == project_acl_policy
|
||||
|
||||
- name: Remove system ACL
|
||||
community.general.rundeck_acl_policy:
|
||||
name: test_acl
|
||||
api_version: "{{ rundeck_api_version }}"
|
||||
url: "{{ rundeck_url }}"
|
||||
token: "{{ rundeck_api_token.stdout_lines[-1] }}"
|
||||
state: absent
|
||||
|
||||
- name: Remove project ACL
|
||||
community.general.rundeck_acl_policy:
|
||||
name: test_acl
|
||||
api_version: "{{ rundeck_api_version }}"
|
||||
url: "{{ rundeck_url }}"
|
||||
token: "{{ rundeck_api_token.stdout_lines[-1] }}"
|
||||
state: absent
|
||||
project: test_project
|
||||
|
||||
- name: Check that ACLs have been removed
|
||||
ansible.builtin.uri:
|
||||
url: "{{ rundeck_url }}/api/{{ rundeck_api_version }}/{{ item }}"
|
||||
headers:
|
||||
accept: application/json
|
||||
x-rundeck-auth-token: "{{ rundeck_api_token.stdout_lines[-1] }}"
|
||||
status_code:
|
||||
- 404
|
||||
loop:
|
||||
- system/acl/test_acl.aclpolicy
|
||||
- project/test_project/acl/test_acl.aclpolicy
|
||||
|
||||
- name: Copy test_job definition to /tmp
|
||||
copy:
|
||||
src: test_job.yaml
|
||||
|
|
|
@ -3,5 +3,13 @@
|
|||
# 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
|
||||
|
||||
rundeck_war_url: https://packagecloud.io/pagerduty/rundeck/packages/java/org.rundeck/rundeck-3.4.4-20210920.war/artifacts/rundeck-3.4.4-20210920.war/download
|
||||
rundeck_cli_url: https://github.com/rundeck/rundeck-cli/releases/download/v1.3.10/rundeck-cli-1.3.10-all.jar
|
||||
rundeck_version: 5.11.1-20250415
|
||||
rundeck_cli_version: "2.0.8"
|
||||
|
||||
rundeck_war_url:
|
||||
"https://packagecloud.io/pagerduty/rundeck/packages/java/org.rundeck/\
|
||||
rundeck-{{ rundeck_version }}.war/artifacts/rundeck-{{ rundeck_version }}.war/download"
|
||||
|
||||
rundeck_cli_url:
|
||||
"https://github.com/rundeck/rundeck-cli/releases/download/\
|
||||
v{{ rundeck_cli_version }}/rundeck-cli-{{ rundeck_cli_version }}-all.jar"
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
# 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
|
||||
|
||||
openjdk_pkg: java-1.8.0-openjdk
|
||||
openjdk_pkg: java-11-openjdk-headless
|
||||
|
|
156
tests/unit/plugins/modules/test_rundeck_acl_policy.py
Normal file
156
tests/unit/plugins/modules/test_rundeck_acl_policy.py
Normal file
|
@ -0,0 +1,156 @@
|
|||
# -*- 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
|
||||
|
||||
import pytest
|
||||
from ansible_collections.community.general.plugins.modules import rundeck_acl_policy
|
||||
from ansible_collections.community.internal_test_tools.tests.unit.compat.mock import patch
|
||||
from ansible_collections.community.internal_test_tools.tests.unit.plugins.modules.utils import (
|
||||
set_module_args,
|
||||
AnsibleExitJson,
|
||||
exit_json,
|
||||
fail_json
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def module():
|
||||
with patch.multiple(
|
||||
"ansible.module_utils.basic.AnsibleModule",
|
||||
exit_json=exit_json,
|
||||
fail_json=fail_json,
|
||||
):
|
||||
yield
|
||||
|
||||
|
||||
# define our two table entries: system ACL vs. project ACL
|
||||
PROJECT_TABLE = [
|
||||
(None, "system/acl"),
|
||||
("test_project", "project/test_project/acl"),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project, prefix", PROJECT_TABLE)
|
||||
@patch.object(rundeck_acl_policy, 'api_request')
|
||||
def test_acl_create(api_request_mock, project, prefix):
|
||||
"""Test creating a new ACL, both system-level and project-level."""
|
||||
name = "my_policy"
|
||||
policy = "test_policy_yaml"
|
||||
# simulate: GET→404, POST→201, final GET→200
|
||||
api_request_mock.side_effect = [
|
||||
(None, {'status': 404}),
|
||||
(None, {'status': 201}),
|
||||
({"contents": policy}, {'status': 200}),
|
||||
]
|
||||
args = {
|
||||
'name': name,
|
||||
'url': "https://rundeck.example.org",
|
||||
'api_token': "mytoken",
|
||||
'policy': policy,
|
||||
}
|
||||
if project:
|
||||
args['project'] = project
|
||||
|
||||
with pytest.raises(AnsibleExitJson):
|
||||
with set_module_args(args):
|
||||
rundeck_acl_policy.main()
|
||||
|
||||
# should have done GET → POST → GET
|
||||
assert api_request_mock.call_count == 3
|
||||
args, kwargs = api_request_mock.call_args_list[1]
|
||||
assert kwargs['endpoint'] == "%s/%s.aclpolicy" % (prefix, name)
|
||||
assert kwargs['method'] == 'POST'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project, prefix", PROJECT_TABLE)
|
||||
@patch.object(rundeck_acl_policy, 'api_request')
|
||||
def test_acl_unchanged(api_request_mock, project, prefix):
|
||||
"""Test no-op when existing ACL contents match the desired policy."""
|
||||
name = "unchanged_policy"
|
||||
policy = "same_policy_yaml"
|
||||
# first GET returns matching contents
|
||||
api_request_mock.return_value = ({"contents": policy}, {'status': 200})
|
||||
|
||||
args = {
|
||||
'name': name,
|
||||
'url': "https://rundeck.example.org",
|
||||
'api_token': "mytoken",
|
||||
'policy': policy,
|
||||
}
|
||||
if project:
|
||||
args['project'] = project
|
||||
|
||||
with pytest.raises(AnsibleExitJson):
|
||||
with set_module_args(args):
|
||||
rundeck_acl_policy.main()
|
||||
|
||||
# only a single GET
|
||||
assert api_request_mock.call_count == 1
|
||||
args, kwargs = api_request_mock.call_args
|
||||
assert kwargs['endpoint'] == "%s/%s.aclpolicy" % (prefix, name)
|
||||
# default method is GET
|
||||
assert kwargs.get('method', 'GET') == 'GET'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project, prefix", PROJECT_TABLE)
|
||||
@patch.object(rundeck_acl_policy, 'api_request')
|
||||
def test_acl_remove(api_request_mock, project, prefix):
|
||||
"""Test removing an existing ACL, both system- and project-level."""
|
||||
name = "remove_me"
|
||||
# GET finds it, DELETE removes it
|
||||
api_request_mock.side_effect = [
|
||||
({"contents": "old_yaml"}, {'status': 200}),
|
||||
(None, {'status': 204}),
|
||||
]
|
||||
|
||||
args = {
|
||||
'name': name,
|
||||
'url': "https://rundeck.example.org",
|
||||
'api_token': "mytoken",
|
||||
'state': 'absent',
|
||||
}
|
||||
if project:
|
||||
args['project'] = project
|
||||
|
||||
with pytest.raises(AnsibleExitJson):
|
||||
with set_module_args(args):
|
||||
rundeck_acl_policy.main()
|
||||
|
||||
# GET → DELETE
|
||||
assert api_request_mock.call_count == 2
|
||||
args, kwargs = api_request_mock.call_args_list[1]
|
||||
assert kwargs['endpoint'] == "%s/%s.aclpolicy" % (prefix, name)
|
||||
assert kwargs['method'] == 'DELETE'
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project, prefix", PROJECT_TABLE)
|
||||
@patch.object(rundeck_acl_policy, 'api_request')
|
||||
def test_acl_remove_nonexistent(api_request_mock, project, prefix):
|
||||
"""Test removing a non-existent ACL results in no change."""
|
||||
name = "not_there"
|
||||
# GET returns 404
|
||||
api_request_mock.return_value = (None, {'status': 404})
|
||||
|
||||
args = {
|
||||
'name': name,
|
||||
'url': "https://rundeck.example.org",
|
||||
'api_token': "mytoken",
|
||||
'state': 'absent',
|
||||
}
|
||||
if project:
|
||||
args['project'] = project
|
||||
|
||||
with pytest.raises(AnsibleExitJson):
|
||||
with set_module_args(args):
|
||||
rundeck_acl_policy.main()
|
||||
|
||||
# only the initial GET
|
||||
assert api_request_mock.call_count == 1
|
||||
args, kwargs = api_request_mock.call_args
|
||||
assert kwargs['endpoint'] == "%s/%s.aclpolicy" % (prefix, name)
|
||||
assert kwargs.get('method', 'GET') == 'GET'
|
Loading…
Add table
Add a link
Reference in a new issue