@ -27,6 +27,7 @@ from .util import (
from . data import (
data_context ,
PayloadConfig ,
)
from . util_common import (
@ -44,11 +45,64 @@ def create_payload(args: CommonConfig, dst_path: str) -> None:
return
files = list ( data_context ( ) . ansible_source )
filters = { }
permissions : dict [ str , int ] = { }
filters : dict [ str , t . Callable [ [ tarfile . TarInfo ] , t . Optional [ tarfile . TarInfo ] ] ] = { }
def apply_permissions ( tar_info : tarfile . TarInfo , mode : int ) - > t . Optional [ tarfile . TarInfo ] :
"""
Apply the specified permissions to the given file .
Existing file type bits are preserved .
"""
tar_info . mode & = ~ ( stat . S_IRWXU | stat . S_IRWXG | stat . S_IRWXO )
tar_info . mode | = mode
return tar_info
def make_executable ( tar_info : tarfile . TarInfo ) - > t . Optional [ tarfile . TarInfo ] :
""" Make the given file executable. """
tar_info . mode | = stat . S_IXUSR | stat . S_IXOTH | stat . S_IXGRP
"""
Make the given file executable and readable by all , and writeable by the owner .
Existing file type bits are preserved .
This ensures consistency of test results when using unprivileged users .
"""
return apply_permissions (
tar_info ,
stat . S_IRUSR | stat . S_IRGRP | stat . S_IROTH |
stat . S_IXUSR | stat . S_IXGRP | stat . S_IXOTH |
stat . S_IWUSR
)
def make_non_executable ( tar_info : tarfile . TarInfo ) - > t . Optional [ tarfile . TarInfo ] :
"""
Make the given file readable by all , and writeable by the owner .
Existing file type bits are preserved .
This ensures consistency of test results when using unprivileged users .
"""
return apply_permissions (
tar_info ,
stat . S_IRUSR | stat . S_IRGRP | stat . S_IROTH |
stat . S_IWUSR
)
def detect_permissions ( tar_info : tarfile . TarInfo ) - > t . Optional [ tarfile . TarInfo ] :
"""
Detect and apply the appropriate permissions for a file .
Existing file type bits are preserved .
This ensures consistency of test results when using unprivileged users .
"""
if tar_info . path . startswith ( ' ansible/ ' ) :
mode = permissions . get ( os . path . relpath ( tar_info . path , ' ansible ' ) )
else :
mode = None
if mode :
tar_info = apply_permissions ( tar_info , mode )
elif tar_info . mode & ( stat . S_IXUSR | stat . S_IXGRP | stat . S_IXOTH ) :
# If any execute bit is set, treat the file as executable.
# This ensures that sanity tests which check execute bits behave correctly.
tar_info = make_executable ( tar_info )
else :
tar_info = make_non_executable ( tar_info )
return tar_info
if not ANSIBLE_SOURCE_ROOT :
@ -85,10 +139,15 @@ def create_payload(args: CommonConfig, dst_path: str) -> None:
# there are no extra files when testing ansible itself
extra_files = [ ]
payload_config = PayloadConfig (
files = content_files ,
permissions = permissions ,
)
for callback in data_context ( ) . payload_callbacks :
# execute callbacks only on the content paths
# this is done before placing them in the appropriate subdirectory (see below)
callback ( content_files )
callback ( payload_config )
# place ansible source files under the 'ansible' directory on the delegated host
files = [ ( src , os . path . join ( ' ansible ' , dst ) ) for src , dst in files ]
@ -109,7 +168,7 @@ def create_payload(args: CommonConfig, dst_path: str) -> None:
with tarfile . open ( dst_path , mode = ' w:gz ' , compresslevel = 4 , format = tarfile . GNU_FORMAT ) as tar :
for src , dst in files :
display . info ( ' %s -> %s ' % ( src , dst ) , verbosity = 4 )
tar . add ( src , dst , filter = filters . get ( dst ))
tar . add ( src , dst , filter = filters . get ( dst , detect_permissions ))
duration = time . time ( ) - start
payload_size_bytes = os . path . getsize ( dst_path )