Merge pull request #9505 from shanemcd/inventory_import-podman

Update inventory_import to run inside of an EE

Option 2 identified in #9504

Reviewed-by: Ryan Petrello <None>
This commit is contained in:
softwarefactory-project-zuul[bot] 2021-03-15 16:47:35 +00:00 committed by GitHub
commit a52f050f44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -4,7 +4,6 @@
# Python
import json
import logging
import fnmatch
import os
import re
import subprocess
@ -34,11 +33,7 @@ from awx.main.utils.safe_yaml import sanitize_jinja
# other AWX imports
from awx.main.models.rbac import batch_role_ancestor_rebuilding
# TODO: remove proot utils once we move to running inv. updates in containers
from awx.main.utils import (
check_proot_installed,
wrap_args_with_proot,
build_proot_temp_dir,
ignore_inventory_computed_fields,
get_licenser
)
@ -89,27 +84,6 @@ class AnsibleInventoryLoader(object):
else:
self.venv_path = settings.ANSIBLE_VENV_PATH
def build_env(self):
env = dict(os.environ.items())
env['VIRTUAL_ENV'] = self.venv_path
env['PATH'] = os.path.join(self.venv_path, "bin") + ":" + env['PATH']
# Set configuration items that should always be used for updates
for key, value in STANDARD_INVENTORY_UPDATE_ENV.items():
if key not in env:
env[key] = value
venv_libdir = os.path.join(self.venv_path, "lib")
env.pop('PYTHONPATH', None) # default to none if no python_ver matches
for version in os.listdir(venv_libdir):
if fnmatch.fnmatch(version, 'python[23].*'):
if os.path.isdir(os.path.join(venv_libdir, version)):
env['PYTHONPATH'] = os.path.join(venv_libdir, version, "site-packages") + ":"
break
# For internal inventory updates, these are not reported in the job_env API
logger.info('Using VIRTUAL_ENV: {}'.format(env['VIRTUAL_ENV']))
logger.info('Using PATH: {}'.format(env['PATH']))
logger.info('Using PYTHONPATH: {}'.format(env.get('PYTHONPATH', None)))
return env
def get_path_to_ansible_inventory(self):
venv_exe = os.path.join(self.venv_path, 'bin', 'ansible-inventory')
if os.path.exists(venv_exe):
@ -128,66 +102,29 @@ class AnsibleInventoryLoader(object):
return shutil.which('ansible-inventory')
def get_base_args(self):
# get ansible-inventory absolute path for running in bubblewrap/proot, in Popen
ansible_inventory_path = self.get_path_to_ansible_inventory()
# NOTE: why do we add "python" to the start of these args?
# the script that runs ansible-inventory specifies a python interpreter
# that makes no sense in light of the fact that we put all the dependencies
# inside of /var/lib/awx/venv/ansible, so we override the specified interpreter
# https://github.com/ansible/ansible/issues/50714
bargs = ['python', ansible_inventory_path, '-i', self.source]
bargs = ['podman', 'run', '--user=root', '--quiet']
bargs.extend(['-v', '{0}:{0}:Z'.format(self.source)])
for key, value in STANDARD_INVENTORY_UPDATE_ENV.items():
bargs.extend(['-e', '{0}={1}'.format(key, value)])
bargs.extend([settings.AWX_CONTAINER_GROUP_DEFAULT_IMAGE])
bargs.extend(['ansible-inventory', '-i', self.source])
bargs.extend(['--playbook-dir', functioning_dir(self.source)])
if self.verbosity:
# INFO: -vvv, DEBUG: -vvvvv, for inventory, any more than 3 makes little difference
bargs.append('-{}'.format('v' * min(5, self.verbosity * 2 + 1)))
bargs.append('--list')
logger.debug('Using base command: {}'.format(' '.join(bargs)))
return bargs
# TODO: Remove this once we move to running ansible-inventory in containers
# and don't need proot for process isolation anymore
def get_proot_args(self, cmd, env):
cwd = os.getcwd()
if not check_proot_installed():
raise RuntimeError("proot is not installed but is configured for use")
kwargs = {}
# we cannot safely store tmp data in source dir or trust script contents
if env['AWX_PRIVATE_DATA_DIR']:
# If this is non-blank, file credentials are being used and we need access
private_data_dir = functioning_dir(env['AWX_PRIVATE_DATA_DIR'])
logger.debug("Using private credential data in '{}'.".format(private_data_dir))
kwargs['private_data_dir'] = private_data_dir
self.tmp_private_dir = build_proot_temp_dir()
logger.debug("Using fresh temporary directory '{}' for isolation.".format(self.tmp_private_dir))
kwargs['proot_temp_dir'] = self.tmp_private_dir
kwargs['proot_show_paths'] = [functioning_dir(self.source), settings.AWX_ANSIBLE_COLLECTIONS_PATHS]
logger.debug("Running from `{}` working directory.".format(cwd))
if self.venv_path != settings.ANSIBLE_VENV_PATH:
kwargs['proot_custom_virtualenv'] = self.venv_path
return wrap_args_with_proot(cmd, cwd, **kwargs)
def command_to_json(self, cmd):
data = {}
stdout, stderr = '', ''
env = self.build_env()
# TODO: remove proot args once inv. updates run in containers
if (('AWX_PRIVATE_DATA_DIR' in env) and
getattr(settings, 'AWX_PROOT_ENABLED', False)):
cmd = self.get_proot_args(cmd, env)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()
stdout = smart_text(stdout)
stderr = smart_text(stderr)
# TODO: can be removed when proot is removed
if self.tmp_private_dir:
shutil.rmtree(self.tmp_private_dir, True)
if proc.returncode != 0:
raise RuntimeError('%s failed (rc=%d) with stdout:\n%s\nstderr:\n%s' % (
'ansible-inventory', proc.returncode, stdout, stderr))
@ -205,9 +142,10 @@ class AnsibleInventoryLoader(object):
def load(self):
base_args = self.get_base_args()
logger.info('Reading Ansible inventory source: %s', self.source)
return self.command_to_json(base_args + ['--list'])
return self.command_to_json(base_args)
class Command(BaseCommand):