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
pull/61292/head
Sloane Hertel 3 years ago committed by GitHub
parent 808f5a0c03
commit cb2e434dd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,2 @@
bugfixes:
- ansible-galaxy - make initial call to Galaxy server on-demand only when installing, getting info about, and listing roles.

@ -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)

@ -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
@ -103,6 +103,14 @@ class GalaxyRole(object):
def __eq__(self, other):
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):
"""

@ -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}" ]]

Loading…
Cancel
Save