diff options
author | Sloane Hertel <19572925+s-hertel@users.noreply.github.com> | 2022-10-14 09:14:35 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-14 09:14:35 -0400 |
commit | cb2e434dd2359a9fe1c00e75431f4abeff7381e8 (patch) | |
tree | 516022d8dfff627af9d1cde4532a6e8d6567bde5 | |
parent | 808f5a0c038ec276f0945013e7ee1f0b2c2b4fbf (diff) | |
download | ansible-cb2e434dd2359a9fe1c00e75431f4abeff7381e8.tar.gz |
ansible-galaxy install - fix unnecessary api check when installing a role from git repo (#79090)
* delay server api evaluation until a GalaxyRole needs to make an api call for info, list, and install
-rw-r--r-- | changelogs/fragments/ansible-galaxy-install-delay-initial-api-call.yml | 2 | ||||
-rwxr-xr-x | lib/ansible/cli/galaxy.py | 62 | ||||
-rw-r--r-- | lib/ansible/galaxy/role.py | 10 | ||||
-rwxr-xr-x | test/integration/targets/ansible-galaxy/runme.sh | 6 |
4 files changed, 55 insertions, 25 deletions
diff --git a/changelogs/fragments/ansible-galaxy-install-delay-initial-api-call.yml b/changelogs/fragments/ansible-galaxy-install-delay-initial-api-call.yml new file mode 100644 index 0000000000..10e8eb8926 --- /dev/null +++ b/changelogs/fragments/ansible-galaxy-install-delay-initial-api-call.yml @@ -0,0 +1,2 @@ +bugfixes: + - ansible-galaxy - make initial call to Galaxy server on-demand only when installing, getting info about, and listing roles. diff --git a/lib/ansible/cli/galaxy.py b/lib/ansible/cli/galaxy.py index acc3f1201c..f4148d924d 100755 --- a/lib/ansible/cli/galaxy.py +++ b/lib/ansible/cli/galaxy.py @@ -17,7 +17,9 @@ import shutil import sys import textwrap import time +import typing as t +from dataclasses import dataclass from yaml.error import YAMLError import ansible.constants as C @@ -170,6 +172,30 @@ def validate_signature_count(value): return value +@dataclass +class RoleDistributionServer: + _api: t.Union[GalaxyAPI, None] + api_servers: list[GalaxyAPI] + + @property + def api(self): + if self._api: + return self._api + + for server in self.api_servers: + try: + if u'v1' in server.available_api_versions: + self._api = server + break + except Exception: + continue + + if not self._api: + self._api = self.api_servers[0] + + return self._api + + class GalaxyCLI(CLI): '''command to manage Ansible roles in shared repositories, the default of which is Ansible Galaxy *https://galaxy.ansible.com*.''' @@ -198,7 +224,7 @@ class GalaxyCLI(CLI): self.api_servers = [] self.galaxy = None - self._api = None + self.lazy_role_api = None super(GalaxyCLI, self).__init__(args) def init_parser(self): @@ -678,25 +704,15 @@ class GalaxyCLI(CLI): **galaxy_options )) + # checks api versions once a GalaxyRole makes an api call + # self.api can be used to evaluate the best server immediately + self.lazy_role_api = RoleDistributionServer(None, self.api_servers) + return context.CLIARGS['func']() @property def api(self): - if self._api: - return self._api - - for server in self.api_servers: - try: - if u'v1' in server.available_api_versions: - self._api = server - break - except Exception: - continue - - if not self._api: - self._api = self.api_servers[0] - - return self._api + return self.lazy_role_api.api def _get_default_collection_path(self): return C.COLLECTIONS_PATHS[0] @@ -757,7 +773,7 @@ class GalaxyCLI(CLI): display.vvv("found role %s in yaml file" % to_text(role)) if "name" not in role and "src" not in role: raise AnsibleError("Must specify name or src for role") - return [GalaxyRole(self.galaxy, self.api, **role)] + return [GalaxyRole(self.galaxy, self.lazy_role_api, **role)] else: b_include_path = to_bytes(requirement["include"], errors="surrogate_or_strict") if not os.path.isfile(b_include_path): @@ -766,7 +782,7 @@ class GalaxyCLI(CLI): with open(b_include_path, 'rb') as f_include: try: - return [GalaxyRole(self.galaxy, self.api, **r) for r in + return [GalaxyRole(self.galaxy, self.lazy_role_api, **r) for r in (RoleRequirement.role_yaml_parse(i) for i in yaml_load(f_include))] except Exception as e: raise AnsibleError("Unable to load data from include requirements file: %s %s" @@ -1182,7 +1198,7 @@ class GalaxyCLI(CLI): for role in context.CLIARGS['args']: role_info = {'path': roles_path} - gr = GalaxyRole(self.galaxy, self.api, role) + gr = GalaxyRole(self.galaxy, self.lazy_role_api, role) install_info = gr.install_info if install_info: @@ -1327,7 +1343,7 @@ class GalaxyCLI(CLI): # (and their dependencies, unless the user doesn't want us to). for rname in context.CLIARGS['args']: role = RoleRequirement.role_yaml_parse(rname.strip()) - role_requirements.append(GalaxyRole(self.galaxy, self.api, **role)) + role_requirements.append(GalaxyRole(self.galaxy, self.lazy_role_api, **role)) if not role_requirements and not collection_requirements: display.display("Skipping install, no requirements found") @@ -1438,7 +1454,7 @@ class GalaxyCLI(CLI): display.debug('Installing dep %s' % dep) dep_req = RoleRequirement() dep_info = dep_req.role_yaml_parse(dep) - dep_role = GalaxyRole(self.galaxy, self.api, **dep_info) + dep_role = GalaxyRole(self.galaxy, self.lazy_role_api, **dep_info) if '.' not in dep_role.name and '.' not in dep_role.src and dep_role.scm is None: # we know we can skip this, as it's not going to # be found on galaxy.ansible.com @@ -1522,7 +1538,7 @@ class GalaxyCLI(CLI): if role_name: # show the requested role, if it exists - gr = GalaxyRole(self.galaxy, self.api, role_name, path=os.path.join(role_path, role_name)) + gr = GalaxyRole(self.galaxy, self.lazy_role_api, role_name, path=os.path.join(role_path, role_name)) if os.path.isdir(gr.path): role_found = True display.display('# %s' % os.path.dirname(gr.path)) @@ -1541,7 +1557,7 @@ class GalaxyCLI(CLI): display.display('# %s' % role_path) path_files = os.listdir(role_path) for path_file in path_files: - gr = GalaxyRole(self.galaxy, self.api, path_file, path=path) + gr = GalaxyRole(self.galaxy, self.lazy_role_api, path_file, path=path) if gr.metadata: _display_role(gr) diff --git a/lib/ansible/galaxy/role.py b/lib/ansible/galaxy/role.py index 7a13c97157..b7bc4077e3 100644 --- a/lib/ansible/galaxy/role.py +++ b/lib/ansible/galaxy/role.py @@ -63,7 +63,7 @@ class GalaxyRole(object): display.debug('Validate TLS certificates: %s' % self._validate_certs) self.galaxy = galaxy - self.api = api + self._api = api self.name = name self.version = version @@ -104,6 +104,14 @@ class GalaxyRole(object): return self.name == other.name @property + def api(self): + # prevent recursive imports + from ansible.cli.galaxy import RoleDistributionServer + if isinstance(self._api, RoleDistributionServer): + return self._api.api + return self._api + + @property def metadata(self): """ Returns role metadata diff --git a/test/integration/targets/ansible-galaxy/runme.sh b/test/integration/targets/ansible-galaxy/runme.sh index 4005531a71..7d966e29e4 100755 --- a/test/integration/targets/ansible-galaxy/runme.sh +++ b/test/integration/targets/ansible-galaxy/runme.sh @@ -103,7 +103,11 @@ f_ansible_galaxy_status "install of local git repo" mkdir -p "${galaxy_testdir}" pushd "${galaxy_testdir}" - ansible-galaxy install git+file:///"${galaxy_local_test_role_git_repo}" "$@" + # minimum verbosity is hardcoded to include calls to Galaxy + ansible-galaxy install git+file:///"${galaxy_local_test_role_git_repo}" "$@" -vvvv 2>&1 | tee out.txt + + # Test no initial call is made to Galaxy + grep out.txt -e "https://galaxy.ansible.com" && cat out.txt && exit 1 # Test that the role was installed to the expected directory [[ -d "${HOME}/.ansible/roles/${galaxy_local_test_role}" ]] |