Jordan Borean 2 weeks ago committed by GitHub
commit d7146905b7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,2 @@
minor_changes:
- ansible-config - Add support for dumping or listing Galaxy server config definitions - https://github.com/ansible/ansible/issues/63288

@ -13,14 +13,17 @@ import shlex
import subprocess
import sys
import yaml
import typing as t
from collections.abc import Mapping
from ansible import context
import ansible.plugins.loader as plugin_loader
from ansible.plugins.loader import PluginLoader
from ansible import constants as C
from ansible.cli.arguments import option_helpers as opt_help
from ansible.cli.galaxy import initialize_galaxy_server_config
from ansible.config.manager import ConfigManager, Setting
from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.module_utils.common.text.converters import to_native, to_text, to_bytes
@ -102,7 +105,8 @@ class ConfigCLI(CLI):
opt_help.add_verbosity_options(common)
common.add_argument('-c', '--config', dest='config_file',
help="path to configuration file, defaults to first file found in precedence.")
common.add_argument("-t", "--type", action="store", default='base', dest='type', choices=['all', 'base'] + list(C.CONFIGURABLE_PLUGINS),
common.add_argument("-t", "--type", action="store", default='base', dest='type',
choices=['all', 'base', 'galaxy_server'] + list(C.CONFIGURABLE_PLUGINS),
help="Filter down to a specific plugin type.")
common.add_argument('args', help='Specific plugin to target, requires type of plugin to be set', nargs='*')
@ -177,6 +181,14 @@ class ConfigCLI(CLI):
elif context.CLIARGS['action'] == 'view':
raise AnsibleError('Invalid or no config file was supplied')
# Galaxy server config is not defined as part of the base.yml config or
# a plugin definition. The defs should be loaded if this type was
# requested.
if context.CLIARGS['type'] in ['all', 'galaxy_server']:
galaxy_server_list = self.config.get_config_value('GALAXY_SERVER_LIST')
if galaxy_server_list:
initialize_galaxy_server_config(self.config, galaxy_server_list)
# run the requested action
context.CLIARGS['func']()
@ -228,32 +240,34 @@ class ConfigCLI(CLI):
except Exception as e:
raise AnsibleError("Failed to open editor: %s" % to_native(e))
def _list_plugin_settings(self, ptype, plugins=None):
def _list_plugin_settings(self, ptype, plugins=None, loader: t.Optional[PluginLoader] = None):
entries = {}
loader = getattr(plugin_loader, '%s_loader' % ptype)
# build list
if plugins:
plugin_cs = []
for plugin in plugins:
p = loader.get(plugin, class_only=True)
if p is None:
display.warning("Skipping %s as we could not find matching plugin" % plugin)
else:
plugin_cs.append(p)
if loader:
if plugins:
plugin_cs = []
for plugin in plugins:
p = loader.get(plugin, class_only=True)
if p is None:
display.warning("Skipping %s as we could not find matching plugin" % plugin)
else:
plugin_cs.append(loader.get(plugin, class_only=True))
else:
plugin_cs = loader.all(class_only=True)
else:
plugin_cs = loader.all(class_only=True)
plugin_cs = plugins or []
# iterate over class instances
for plugin in plugin_cs:
finalname = name = plugin._load_name
if name.startswith('_'):
# alias or deprecated
# in case of deprecastion they diverge
finalname = name = getattr(plugin, "_load_name", plugin)
if name.startswith('_') and hasattr(plugin, "_original_path"):
if os.path.islink(plugin._original_path):
# skip alias
continue
else:
finalname = name.replace('_', '', 1) + ' (DEPRECATED)'
# deprecated, but use 'nice name'
finalname = name.replace('_', '', 1) + ' (DEPRECATED)'
# default entries per plugin
entries[finalname] = self.config.get_configuration_definitions(ptype, name)
return entries
@ -267,16 +281,26 @@ class ConfigCLI(CLI):
# this dumps main/common configs
config_entries = self.config.get_configuration_definitions(ignore_private=True)
if context.CLIARGS['type'] != 'base':
if context.CLIARGS['type'] in ('galaxy_server', 'all'):
server_list = context.CLIARGS['args'] or self.config.get_config_value('GALAXY_SERVER_LIST')
config_entries['GALAXY_SERVERS'] = self._list_plugin_settings('galaxy_server', server_list)
if context.CLIARGS['type'] not in ('base', 'galaxy_server'):
config_entries['PLUGINS'] = {}
if context.CLIARGS['type'] == 'all':
# now each plugin type
for ptype in C.CONFIGURABLE_PLUGINS:
config_entries['PLUGINS'][ptype.upper()] = self._list_plugin_settings(ptype)
elif context.CLIARGS['type'] != 'base':
loader = getattr(plugin_loader, f'{ptype}_loader')
config_entries['PLUGINS'][ptype.upper()] = self._list_plugin_settings(ptype, loader=loader)
elif context.CLIARGS['type'] not in ('base', 'galaxy_server'):
# only for requested types
config_entries['PLUGINS'][context.CLIARGS['type']] = self._list_plugin_settings(context.CLIARGS['type'], context.CLIARGS['args'])
loader = getattr(plugin_loader, f'{context.CLIARGS["type"]}_loader')
config_entries['PLUGINS'][context.CLIARGS['type']] = self._list_plugin_settings(
context.CLIARGS['type'],
context.CLIARGS['args'],
loader=loader,
)
return config_entries
@ -483,49 +507,23 @@ class ConfigCLI(CLI):
return self._render_settings(config)
def _get_plugin_configs(self, ptype, plugins):
# prep loading
loader = getattr(plugin_loader, '%s_loader' % ptype)
def _get_plugin_configs(self, ptype, plugins, loader: t.Optional[PluginLoader] = None):
# acumulators
output = []
config_entries = {}
output: t.List[t.Union[str, t.Dict]] = []
config_entries = self._list_plugin_settings(ptype, plugins, loader=loader)
# build list
if plugins:
plugin_cs = []
for plugin in plugins:
p = loader.get(plugin, class_only=True)
if p is None:
display.warning("Skipping %s as we could not find matching plugin" % plugin)
else:
plugin_cs.append(loader.get(plugin, class_only=True))
else:
plugin_cs = loader.all(class_only=True)
for plugin in plugin_cs:
# in case of deprecastion they diverge
finalname = name = plugin._load_name
if name.startswith('_'):
if os.path.islink(plugin._original_path):
# skip alias
for name in config_entries.keys():
if loader:
try:
# populate config entries by loading plugin
dump = loader.get(name, class_only=True)
except Exception as e:
display.warning('Skipping "%s" %s plugin, as we cannot load plugin to check config due to : %s' % (name, ptype, to_native(e)))
continue
# deprecated, but use 'nice name'
finalname = name.replace('_', '', 1) + ' (DEPRECATED)'
# default entries per plugin
config_entries[finalname] = self.config.get_configuration_definitions(ptype, name)
try:
# populate config entries by loading plugin
dump = loader.get(name, class_only=True)
except Exception as e:
display.warning('Skipping "%s" %s plugin, as we cannot load plugin to check config due to : %s' % (name, ptype, to_native(e)))
continue
# actually get the values
for setting in config_entries[finalname].keys():
for setting in config_entries[name].keys():
try:
v, o = C.config.get_config_value_and_origin(setting, cfile=self.config_file, plugin_type=ptype, plugin_name=name, variables=get_constants())
except AnsibleError as e:
@ -539,17 +537,17 @@ class ConfigCLI(CLI):
# not all cases will be error
o = 'REQUIRED'
config_entries[finalname][setting] = Setting(setting, v, o, None)
config_entries[name][setting] = Setting(setting, v, o, None)
# pretty please!
results = self._render_settings(config_entries[finalname])
results = self._render_settings(config_entries[name])
if results:
if context.CLIARGS['format'] == 'display':
# avoid header for empty lists (only changed!)
output.append('\n%s:\n%s' % (finalname, '_' * len(finalname)))
output.append('\n%s:\n%s' % (name, '_' * len(name)))
output.extend(results)
else:
output.append({finalname: results})
output.append({name: results})
return output
@ -563,22 +561,41 @@ class ConfigCLI(CLI):
elif context.CLIARGS['type'] == 'all':
# deal with base
output = self._get_global_configs()
# deal with plugins
for ptype in C.CONFIGURABLE_PLUGINS:
plugin_list = self._get_plugin_configs(ptype, context.CLIARGS['args'])
for ptype in list(C.CONFIGURABLE_PLUGINS) + ['galaxy_server']:
loader = None
if ptype == 'galaxy_server':
loader = None
plugins = context.CLIARGS['args'] or self.config.get_config_value('GALAXY_SERVER_LIST')
else:
loader = getattr(plugin_loader, '%s_loader' % ptype)
plugins = context.CLIARGS['args']
plugin_list = self._get_plugin_configs(ptype, plugins, loader=loader)
if context.CLIARGS['format'] == 'display':
if not context.CLIARGS['only_changed'] or plugin_list:
output.append('\n%s:\n%s' % (ptype.upper(), '=' * len(ptype)))
output.extend(plugin_list)
else:
if ptype in ('modules', 'doc_fragments'):
if ptype in ('galaxy_server', 'modules', 'doc_fragments'):
pname = ptype.upper()
else:
pname = '%s_PLUGINS' % ptype.upper()
output.append({pname: plugin_list})
else:
# deal with plugins
output = self._get_plugin_configs(context.CLIARGS['type'], context.CLIARGS['args'])
loader = None
if context.CLIARGS['type'] == 'galaxy_server':
loader = None
plugins = context.CLIARGS['args'] or self.config.get_config_value('GALAXY_SERVER_LIST')
else:
loader = getattr(plugin_loader, '%s_loader' % context.CLIARGS['type'])
plugins = context.CLIARGS['args']
output = self._get_plugin_configs(context.CLIARGS['type'], plugins, loader=loader)
if context.CLIARGS['format'] == 'display':
text = '\n'.join(output)

@ -27,6 +27,7 @@ from yaml.error import YAMLError
import ansible.constants as C
from ansible import context
from ansible.cli.arguments import option_helpers as opt_help
from ansible.config.manager import ConfigManager
from ansible.errors import AnsibleError, AnsibleOptionsError
from ansible.galaxy import Galaxy, get_collections_galaxy_meta_info
from ansible.galaxy.api import GalaxyAPI, GalaxyError
@ -88,6 +89,40 @@ SERVER_ADDITIONAL = {
}
def initialize_galaxy_server_config(config: ConfigManager, server_list: t.List[str]) -> None:
def server_config_def(section, key, required, option_type):
config_def = {
'description': 'The %s of the %s Galaxy server' % (key, section),
'ini': [
{
'section': 'galaxy_server.%s' % section,
'key': key,
}
],
'env': [
{'name': 'ANSIBLE_GALAXY_SERVER_%s_%s' % (section.upper(), key.upper())},
],
'required': required,
'type': option_type,
}
if key in SERVER_ADDITIONAL:
config_def.update(SERVER_ADDITIONAL[key])
return config_def
# Need to filter out empty strings or non truthy values as an empty server list env var is equal to [''].
for server_key in server_list:
if not server_key:
continue
# Abuse the 'plugin config' by making 'galaxy_server' a type of plugin
# Config definitions are looked up dynamically based on the C.GALAXY_SERVER_LIST entry. We look up the
# section [galaxy_server.<server>] for the values url, username, password, and token.
config_dict = dict((k, server_config_def(server_key, k, req, ensure_type)) for k, req, ensure_type in SERVER_DEF)
defs = AnsibleLoader(yaml_dump(config_dict)).get_single_data()
config.initialize_plugin_configuration_definitions('galaxy_server', server_key, defs)
def with_collection_artifacts_manager(wrapped_method):
"""Inject an artifacts manager if not passed explicitly.
@ -618,25 +653,9 @@ class GalaxyCLI(CLI):
self.galaxy = Galaxy()
def server_config_def(section, key, required, option_type):
config_def = {
'description': 'The %s of the %s Galaxy server' % (key, section),
'ini': [
{
'section': 'galaxy_server.%s' % section,
'key': key,
}
],
'env': [
{'name': 'ANSIBLE_GALAXY_SERVER_%s_%s' % (section.upper(), key.upper())},
],
'required': required,
'type': option_type,
}
if key in SERVER_ADDITIONAL:
config_def.update(SERVER_ADDITIONAL[key])
return config_def
# Need to filter out empty strings or non truthy values as an empty server list env var is equal to [''].
server_list = [s for s in C.GALAXY_SERVER_LIST or [] if s]
initialize_galaxy_server_config(C.config, server_list)
galaxy_options = {}
for optional_key in ['clear_response_cache', 'no_cache']:
@ -645,15 +664,7 @@ class GalaxyCLI(CLI):
config_servers = []
# Need to filter out empty strings or non truthy values as an empty server list env var is equal to [''].
server_list = [s for s in C.GALAXY_SERVER_LIST or [] if s]
for server_priority, server_key in enumerate(server_list, start=1):
# Abuse the 'plugin config' by making 'galaxy_server' a type of plugin
# Config definitions are looked up dynamically based on the C.GALAXY_SERVER_LIST entry. We look up the
# section [galaxy_server.<server>] for the values url, username, password, and token.
config_dict = dict((k, server_config_def(server_key, k, req, ensure_type)) for k, req, ensure_type in SERVER_DEF)
defs = AnsibleLoader(yaml_dump(config_dict)).get_single_data()
C.config.initialize_plugin_configuration_definitions('galaxy_server', server_key, defs)
# resolve the config created options above with existing config and user options
server_options = C.config.get_plugin_options('galaxy_server', server_key)

@ -0,0 +1,21 @@
[galaxy]
server_list = my_org_hub, release_galaxy, test_galaxy, my_galaxy_ng
[galaxy_server.my_org_hub]
url=https://automation.my_org/
username=my_user
password=my_pass
[galaxy_server.release_galaxy]
url=https://galaxy.ansible.com/
token=my_token
[galaxy_server.test_galaxy]
url=https://galaxy-dev.ansible.com/
token=my_test_token
[galaxy_server.my_galaxy_ng]
url=http://my_galaxy_ng:8000/api/automation-hub/
auth_url=http://my_keycloak:8080/auth/realms/myco/protocol/openid-connect/token
client_id=galaxy-ng
token=my_keycloak_access_token

@ -56,3 +56,55 @@
that:
- valid_env is success
- invalid_env is failed
- name: dump galaxy_server config
ansible.builtin.command: ansible-config dump --type {{ item }} --format json
environment:
ANSIBLE_CONFIG: '{{ role_path }}/files/galaxy_server.ini'
loop:
- galaxy_server
- all
register: galaxy_server_dump
- set_fact:
galaxy_server_dump_gs: '{{ (galaxy_server_dump.results[1].stdout | from_json | selectattr("GALAXY_SERVER", "defined") | first).GALAXY_SERVER }}'
galaxy_server_dump_all: '{{ (galaxy_server_dump.results[1].stdout | from_json | selectattr("GALAXY_SERVER", "defined") | first).GALAXY_SERVER }}'
- name: assert galaxy server config
assert:
that:
- (galaxy_server_dump_gs | count) == 4
- galaxy_server_dump_gs[0].keys() | list == ["my_org_hub"]
- (galaxy_server_dump_gs[0].my_org_hub | selectattr("name", "equalto", "url"))[0].value == "https://automation.my_org/"
- (galaxy_server_dump_gs[0].my_org_hub | selectattr("name", "equalto", "url"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- (galaxy_server_dump_gs[0].my_org_hub | selectattr("name", "equalto", "username"))[0].value == "my_user"
- (galaxy_server_dump_gs[0].my_org_hub | selectattr("name", "equalto", "username"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- (galaxy_server_dump_gs[0].my_org_hub | selectattr("name", "equalto", "password"))[0].value == "my_pass"
- (galaxy_server_dump_gs[0].my_org_hub | selectattr("name", "equalto", "password"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- (galaxy_server_dump_gs[0].my_org_hub | selectattr("name", "equalto", "api_version"))[0].value == None
- (galaxy_server_dump_gs[0].my_org_hub | selectattr("name", "equalto", "api_version"))[0].origin == "default"
- galaxy_server_dump_gs[1].keys() | list == ["release_galaxy"]
- (galaxy_server_dump_gs[1].release_galaxy | selectattr("name", "equalto", "url"))[0].value == "https://galaxy.ansible.com/"
- (galaxy_server_dump_gs[1].release_galaxy | selectattr("name", "equalto", "url"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- (galaxy_server_dump_gs[1].release_galaxy | selectattr("name", "equalto", "token"))[0].value == "my_token"
- (galaxy_server_dump_gs[1].release_galaxy | selectattr("name", "equalto", "token"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- galaxy_server_dump_gs[2].keys() | list == ["test_galaxy"]
- (galaxy_server_dump_gs[2].test_galaxy | selectattr("name", "equalto", "url"))[0].value == "https://galaxy-dev.ansible.com/"
- (galaxy_server_dump_gs[2].test_galaxy | selectattr("name", "equalto", "url"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- (galaxy_server_dump_gs[2].test_galaxy | selectattr("name", "equalto", "token"))[0].value == "my_test_token"
- (galaxy_server_dump_gs[2].test_galaxy | selectattr("name", "equalto", "token"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- galaxy_server_dump_gs[3].keys() | list == ["my_galaxy_ng"]
- (galaxy_server_dump_gs[3].my_galaxy_ng | selectattr("name", "equalto", "url"))[0].value == "http://my_galaxy_ng:8000/api/automation-hub/"
- (galaxy_server_dump_gs[3].my_galaxy_ng | selectattr("name", "equalto", "url"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- (galaxy_server_dump_gs[3].my_galaxy_ng | selectattr("name", "equalto", "token"))[0].value == "my_keycloak_access_token"
- (galaxy_server_dump_gs[3].my_galaxy_ng | selectattr("name", "equalto", "token"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- (galaxy_server_dump_gs[3].my_galaxy_ng | selectattr("name", "equalto", "auth_url"))[0].value == "http://my_keycloak:8080/auth/realms/myco/protocol/openid-connect/token"
- (galaxy_server_dump_gs[3].my_galaxy_ng | selectattr("name", "equalto", "auth_url"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- (galaxy_server_dump_gs[3].my_galaxy_ng | selectattr("name", "equalto", "client_id"))[0].value == "galaxy-ng"
- (galaxy_server_dump_gs[3].my_galaxy_ng | selectattr("name", "equalto", "client_id"))[0].origin == role_path ~ "/files/galaxy_server.ini"
- galaxy_server_dump_gs == galaxy_server_dump_all

Loading…
Cancel
Save