-
Notifications
You must be signed in to change notification settings - Fork 90
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add extra sanity test for acme action group.
- Loading branch information
1 parent
ccb4ecf
commit f956ddc
Showing
3 changed files
with
138 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"include_symlinks": true, | ||
"prefixes": [ | ||
"meta/runtime.yml", | ||
"plugins/modules/", | ||
"tests/sanity/extra/action-group." | ||
], | ||
"output": "path-message", | ||
"requirements": [ | ||
"pyyaml" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
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 | ||
SPDX-FileCopyrightText: Ansible Project |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
#!/usr/bin/env python | ||
# Copyright (c) 2024, Felix Fontein <felix@fontein.de> | ||
# 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 | ||
"""Make sure all modules that should show up in the action group.""" | ||
|
||
from __future__ import annotations | ||
|
||
import os | ||
import re | ||
import yaml | ||
|
||
|
||
ACTION_GROUPS = { | ||
# The format is as follows: | ||
# * 'pattern': a regular expression matching all module names potentially belonging to the action group; | ||
# * 'exclusions': a list of modules that are not part of the action group; all other modules matching 'pattern' must be part of it; | ||
# * 'doc_fragment': the docs fragment that documents membership of the action group. | ||
'acme': { | ||
'pattern': re.compile('^acme_.*$'), | ||
'exclusions': [ | ||
'acme_ari_info', # does not support ACME account | ||
'acme_certificate_renewal_info', # does not support ACME account | ||
'acme_challenge_cert_helper', # does not support (and need) any common parameters | ||
], | ||
'doc_fragment': 'community.crypto.attributes.actiongroup_acme', | ||
}, | ||
} | ||
|
||
|
||
def main(): | ||
"""Main entry point.""" | ||
|
||
# Load redirects | ||
meta_runtime = 'meta/runtime.yml' | ||
self_path = 'tests/sanity/extra/action-group.py' | ||
try: | ||
with open(meta_runtime, 'rb') as f: | ||
data = yaml.safe_load(f) | ||
action_groups = data['action_groups'] | ||
except Exception as exc: | ||
print(f'{meta_runtime}: cannot load action groups: {exc}') | ||
return | ||
|
||
for action_group in action_groups: | ||
if action_group not in ACTION_GROUPS: | ||
print(f'{meta_runtime}: found unknown action group {action_group!r}; likely {self_path} needs updating') | ||
for action_group, action_group_data in list(ACTION_GROUPS.items()): | ||
if action_group not in action_groups: | ||
print(f'{meta_runtime}: cannot find action group {action_group!r}; likely {self_path} needs updating') | ||
|
||
modules_directory = 'plugins/modules/' | ||
modules_suffix = '.py' | ||
|
||
for file in os.listdir(modules_directory): | ||
if not file.endswith(modules_suffix): | ||
continue | ||
module_name = file[:-len(modules_suffix)] | ||
|
||
for action_group, action_group_data in ACTION_GROUPS.items(): | ||
action_group_content = action_groups.get(action_group) or [] | ||
path = os.path.join(modules_directory, file) | ||
|
||
if not action_group_data['pattern'].match(module_name): | ||
if module_name in action_group_content: | ||
print(f'{path}: module is in action group {action_group!r} despite not matching its pattern as defined in {self_path}') | ||
continue | ||
|
||
should_be_in_action_group = module_name not in action_group_data['exclusions'] | ||
|
||
if should_be_in_action_group: | ||
if module_name not in action_group_content: | ||
print(f'{meta_runtime}: module {module_name!r} is not part of {action_group!r} action group') | ||
else: | ||
action_group_content.remove(module_name) | ||
|
||
documentation = [] | ||
in_docs = False | ||
with open(path, 'r', encoding='utf-8') as f: | ||
for line in f: | ||
if line.startswith('DOCUMENTATION ='): | ||
in_docs = True | ||
elif line.startswith(("'''", '"""')) and in_docs: | ||
in_docs = False | ||
elif in_docs: | ||
documentation.append(line) | ||
if in_docs: | ||
print(f'{path}: cannot find DOCUMENTATION end') | ||
if not documentation: | ||
print(f'{path}: cannot find DOCUMENTATION') | ||
continue | ||
|
||
try: | ||
docs = yaml.safe_load('\n'.join(documentation)) | ||
if not isinstance(docs, dict): | ||
raise Exception('is not a top-level dictionary') | ||
except Exception as exc: | ||
print(f'{path}: cannot load DOCUMENTATION as YAML: {exc}') | ||
continue | ||
|
||
docs_fragments = docs.get('extends_documentation_fragment') or [] | ||
is_in_action_group = action_group_data['doc_fragment'] in docs_fragments | ||
|
||
if should_be_in_action_group != is_in_action_group: | ||
if should_be_in_action_group: | ||
print( | ||
f'{path}: module does not document itself as part of action group {action_group!r}, but it should;' | ||
f' you need to add {action_group_data["doc_fragment"]} to "extends_documentation_fragment" in DOCUMENTATION' | ||
) | ||
else: | ||
print(f'{path}: module documents itself as part of action group {action_group!r}, but it should not be') | ||
|
||
for action_group, action_group_data in ACTION_GROUPS.items(): | ||
action_group_content = action_groups.get(action_group) or [] | ||
for module_name in action_group_content: | ||
print( | ||
f'{meta_runtime}: module {module_name} mentioned in {action_group!r} action group' | ||
f' does not exist or does not match pattern defined in {self_path}' | ||
) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |