Merge pull request #11659 from ansible/expose_isolate_path_k8s

Allow isolated paths as hostPath volume @ k8s/ocp/container groups
This commit is contained in:
Marcelo Moreira de Mello
2022-03-01 10:52:36 -05:00
committed by GitHub
11 changed files with 98 additions and 5 deletions

View File

@@ -334,6 +334,19 @@ register(
category_slug='jobs',
)
register(
'AWX_MOUNT_ISOLATED_PATHS_ON_K8S',
field_class=fields.BooleanField,
default=False,
label=_('Expose host paths for Container Groups'),
help_text=_(
'Expose paths via hostPath for the Pods created by a Container Group. '
'HostPath volumes present many security risks, and it is a best practice to avoid the use of HostPaths when possible. '
),
category=_('Jobs'),
category_slug='jobs',
)
register(
'GALAXY_IGNORE_CERTS',
field_class=fields.BooleanField,

View File

@@ -88,7 +88,8 @@ JOB_FOLDER_PREFIX = 'awx_%s_'
# :z option tells Podman that two containers share the volume content with r/w
# :O option tells Podman to mount the directory from the host as a temporary storage using the overlay file system.
# :ro or :rw option to mount a volume in read-only or read-write mode, respectively. By default, the volumes are mounted read-write.
# see podman-run manpage for further details
# /HOST-DIR:/CONTAINER-DIR:OPTIONS
CONTAINER_VOLUMES_MOUNT_TYPES = ['z', 'O']
CONTAINER_VOLUMES_MOUNT_TYPES = ['z', 'O', 'ro', 'rw']
MAX_ISOLATED_PATH_COLON_DELIMITER = 2

View File

@@ -40,6 +40,7 @@ from awx.main.constants import (
STANDARD_INVENTORY_UPDATE_ENV,
JOB_FOLDER_PREFIX,
MAX_ISOLATED_PATH_COLON_DELIMITER,
CONTAINER_VOLUMES_MOUNT_TYPES,
)
from awx.main.models import (
Instance,
@@ -163,8 +164,14 @@ class BaseTask(object):
# Using z allows the dir to be mounted by multiple containers
# Uppercase Z restricts access (in weird ways) to 1 container at a time
if this_path.count(':') == MAX_ISOLATED_PATH_COLON_DELIMITER:
src, dest, scontext = this_path.split(':')
params['container_volume_mounts'].append(f'{src}:{dest}:{scontext}')
src, dest, mount_option = this_path.split(':')
# mount_option validation via performed via API, but since this can be overriden via settings.py
if mount_option not in CONTAINER_VOLUMES_MOUNT_TYPES:
mount_option = 'z'
logger.warn(f'The path {this_path} has volume mount type {mount_option} which is not supported. Using "z" instead.')
params['container_volume_mounts'].append(f'{src}:{dest}:{mount_option}')
elif this_path.count(':') == MAX_ISOLATED_PATH_COLON_DELIMITER - 1:
src, dest = this_path.split(':')
params['container_volume_mounts'].append(f'{src}:{dest}:z')

View File

@@ -24,6 +24,8 @@ from awx.main.utils.common import (
parse_yaml_or_json,
cleanup_new_process,
)
from awx.main.constants import MAX_ISOLATED_PATH_COLON_DELIMITER
# Receptorctl
from receptorctl.socket_interface import ReceptorControl
@@ -478,6 +480,48 @@ class AWXReceptorJob:
if self.task.instance.execution_environment.pull:
pod_spec['spec']['containers'][0]['imagePullPolicy'] = pull_options[self.task.instance.execution_environment.pull]
# This allows the user to also expose the isolated path list
# to EEs running in k8s/ocp environments, i.e. container groups.
# This assumes the node and SA supports hostPath volumes
# type is not passed due to backward compatibility,
# which means that no checks will be performed before mounting the hostPath volume.
if settings.AWX_MOUNT_ISOLATED_PATHS_ON_K8S and settings.AWX_ISOLATION_SHOW_PATHS:
spec_volume_mounts = []
spec_volumes = []
for idx, this_path in enumerate(settings.AWX_ISOLATION_SHOW_PATHS):
mount_option = None
if this_path.count(':') == MAX_ISOLATED_PATH_COLON_DELIMITER:
src, dest, mount_option = this_path.split(':')
elif this_path.count(':') == MAX_ISOLATED_PATH_COLON_DELIMITER - 1:
src, dest = this_path.split(':')
else:
src = dest = this_path
# Enforce read-only volume if 'ro' has been explicitly passed
# We do this so we can use the same configuration for regular scenarios and k8s
# Since flags like ':O', ':z' or ':Z' are not valid in the k8s realm
# Example: /data:/data:ro
read_only = bool('ro' == mount_option)
# Since type is not being passed, k8s by default will not perform any checks if the
# hostPath volume exists on the k8s node itself.
spec_volumes.append({'name': f'volume-{idx}', 'hostPath': {'path': src}})
spec_volume_mounts.append({'name': f'volume-{idx}', 'mountPath': f'{dest}', 'readOnly': read_only})
# merge any volumes definition already present in the pod_spec
if 'volumes' in pod_spec['spec']:
pod_spec['spec']['volumes'] += spec_volumes
else:
pod_spec['spec']['volumes'] = spec_volumes
# merge any volumesMounts definition already present in the pod_spec
if 'volumeMounts' in pod_spec['spec']['containers'][0]:
pod_spec['spec']['containers'][0]['volumeMounts'] += spec_volume_mounts
else:
pod_spec['spec']['containers'][0]['volumeMounts'] = spec_volume_mounts
if self.task and self.task.instance.is_container_group_task:
# If EE credential is passed, create an imagePullSecret
if self.task.instance.execution_environment and self.task.instance.execution_environment.credential: