Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ansible/ansible.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSloane Hertel <19572925+s-hertel@users.noreply.github.com>2022-10-14 16:14:35 +0300
committerGitHub <noreply@github.com>2022-10-14 16:14:35 +0300
commitcb2e434dd2359a9fe1c00e75431f4abeff7381e8 (patch)
tree516022d8dfff627af9d1cde4532a6e8d6567bde5
parent808f5a0c038ec276f0945013e7ee1f0b2c2b4fbf (diff)
ansible-galaxy install - fix unnecessary api check when installing a role from git repo (#79090)milestone
* 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.yml2
-rwxr-xr-xlib/ansible/cli/galaxy.py62
-rw-r--r--lib/ansible/galaxy/role.py10
-rwxr-xr-xtest/integration/targets/ansible-galaxy/runme.sh6
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 00000000000..10e8eb8926c
--- /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 acc3f1201ca..f4148d924db 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 7a13c971575..b7bc4077e3d 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 4005531a712..7d966e29e42 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}" ]]